今天在这里我就说说java中的泛型,首先我们要先了解几个基本的问题
1.什么是泛型?为什么会出现泛型?
在说这个问题之前,我们先说一下List集合,我们在使用集合的时候,基本都会在后面加上一对尖括号,并在里面指定一个类型,例如List < String> ,表示这个集合中只能存放String类型的变量,这也是我们要说的泛型,在java5之前是并没有泛型的,在没有泛型的年代,如果我们要往List集合中存入元素的时候,无论你存入的是什么元素,List都会把它当成Object类型来处理,所以当从集合中取出元素的时候还需要进行类型的转换,这样会使代码臃肿且会降低性能,如果List集合中存入的不是同一种元素,还会造成ClassCastExeception异常。综上所述,泛型就是为了减去程序员编程出错而出现的一种方法。
2.泛型的使用
从字面上来看,泛型就是泛指一种类型,他的使用也是十分的灵活的,泛型可以使用在类,接口,方法,方法的形参等位置
public class Demo2 {
public static void main(String[] args) {
ArrayList<String> list =new ArrayList<String>();
list.add("lex");
//会出现编译错误
list.(1);
}
}
例如上面代码,集合中的泛型定义为String类型,如果试图将Integer类型的变量添加进去的话就会造成编译异常,会使编译无法通过,一定程度上保证了安全和代码的健壮性。
3.泛型中的继承问题
例如 C继承P ,C是P的子类,但List< C>并不是List< P>的子类,也就是说并不能用List< P>的类型来接收List< C>的类型变量
public class Demo2 {
public static void main(String[] args) {
List<String> list =new ArrayList<String>();
//编译不通过
//The method test(List<Object>) in the type Demo2 is not applicable for the arguments (List<String>)
test(list);
}
public static void test(List<Object> list){
System.out.println("test");
}
}
例如上面的代码,在该类中定义了一个静态的test方法,用来接收List< Object>的参数,我在调用该方法的时候传入了一个List< String >的集合,按照正常的习惯来看,String是Object的子类,所以用父类接收子类引用应该是可行的,但是编译器却报了参数类型不匹配的异常,也就是我们前面说的List< String >并不是List< Object>的子类。
4.通配符的使用
通配符的表示方法就是为一个问号?,例如List<?>,这个可以用来表示各种List泛型的父类,这个List集合中的元素类型可以匹配任何的元素类型,需要注意的是List<?>这个集合中无法调用add方法进行元素的添加,因为程序并没有办法确定这个List集合中的元素类型,根据前面的List< T>,他的add方法中的形参为add(T t),这里他的参数就是已知的,而使用通配符,无法知道元素的类型,所以没有办法添加元素,但是可以取出元素,其实在取出元素的时候,这个元素对程序来说仍然是未知的,但是无论他是什么类型,他一定会使Object的子类,所以get的返回值类型用Object来接受就没有问题了。
5.类型通配符的上限
在前面我们使用List<?>可以代表任何泛型List的父类,但有些时候,我们并不希望这个List是任何泛型List的父类,我们只希望他是一部分类型的父类,这里我们就要使用到类型通配符的上限了,具体的使用方法为<? extends 要指定的上限类>
public class Demo2 {
public static void main(String[] args) {
List<? extends A> list =new ArrayList<A>();
List<? extends A> list1 =new ArrayList<B>();
List<? extends A> list2 =new ArrayList<C>();
//编译出错 Type mismatch: cannot convert from ArrayList<D> to List<? extends A>
List<? extends A> list3 =new ArrayList<D>();
}
}
class A{
}
class B extends A{
}
class C extends A{
}
class D{
}
上面的代码,首先在外面定义了ABCD四个类,其中 BC是继承A类的 D类是单独的一个类,和ABC没有关系,在List集合中定义了一个上限,这个含义就是这个集合的泛型中只能传入A类或者是他的子类,上面分别将ABC传入,没有出现编译异常,当把D传入时出现了类型匹配失败的异常,这样也就约束了泛型中类的类型,而不是我们之前所说的传入所有泛型的集合了。这里的A就是类型通配符的上限。
而这种规定上限的集合同样是只能取不能添,因为这里虽然指定了上限,但是我们的程序仍然无法得知A类型的子类究竟是什么,仍然是未知的,所以和前面说的类型通配符的原理相同。
前面我们说到 B是A的子类,但List< B>并不是List< A>的子类,但是List< B>是List<? extends A>的子类,所以可以将List< B>类型的变量赋值给List<? extends A>的变量,这一过程被称作协变。
6.类型通配符的下限
类型通配符除了可以指定上限,还可以指定下限,写法<? super 类型 >,和上限相反,程序可以将List< Object>赋值给List<? super String>,这一过程被成为逆变。
对于这种逆变得集合类型,程序只知道集合中的元素是下限的父类,但并不知道具体是哪一种类型,所以这种集合可以向其中添加元素,而取出的元素的类型仍为Object类型

11万+

被折叠的 条评论
为什么被折叠?



