在集合中使用泛型是经常见到的,之前对这一块也是懵懵懂懂,这几天研究了一下,下面总结一下:
1.List、List<T>、List<?>的区别
List与List<Object>
很多人认为List 就是List<Object>类型的集合,毕竟二者的行为绝大情况下是一样的:都可以放进去任意类型的对象,而取出来的对象都是Object类型。
List l = new ArrayList();
l.add(new Animal());
l.add(new Dog());
Object o = l.get(0);
List<Object> objectList = new ArrayList<>();
objectList.add(new Animal());
objectList.add(new Dog());
Object o1 = objectList.get(0);
但是二者的行为还是有所不同的。
List表示没有泛型类型,该List可以装任何东西,它就没有涉及到泛型这个概念。List:别跟我谈什么泛型,我这里就没有这个东西。Java在没有引入泛型之前,List就是这么用的。
List<Object>就很清楚了,表示Object类型的List,它也可以装所有类型的对象,那是因为Object是Java中所有类的基类,是Java多态才导致它可以装所有类型的对象的。
所以在某些情况下二者还是会有所不一样的:
// 因为l是没有类型的,所以它可以赋值给任意类型的List引用
// 这也是一种Java引入泛型以后对之前代码的兼容
List<? extends Animal> list = l;
// 下面这个这段代码是很危险的,因为list是什么类型的对象都有的
// 并不都是Animal类型的对象,这是Java为了向下兼容导致的问题
final Animal animal = list.get(0);
// 下面这行代码会报错,因为objectList已经指明了是Object类型的List,
// 而list只能引用Animal及其子类类型的List
list = objectList;
所以,List因为没有泛型类型,是可以赋值给任意的泛型List引用的,而List<Object>就不行。
如果T是一种具体的类型,例如List<Animal>,那么意义就更加明确了,就是该List里面装的都是Animal,不能是其他类型的对象。
List<?>
List<?> 表示一个泛型通配类型的引用,它可以引用其他任意类型泛型的List,所以它可以引用List和List<Object>。
List<?>和List<Obejct>的不同在于,List<Obejct>仅仅表示它里面的元素是Object类型的,它用于限制List内部元素的类型;而List<?>作用于List的泛型类型,不管你是什么泛型类型的List,都可以用List<?>来表示。List<?>一般作为参数来接收外部的集合,或者返回一个不知道具体泛型类型的集合。
因此List<?>可以引用List<Animal>,而List<Object>则不能,因为List<Object>和List<Animal>并不是同一个类型。
但List不能添加任何非null元素,但可以remove和clear等移除操作。这是因为List<?>肯定是某个泛型类型的List,但不知道具体是哪一个泛型类型,即List<?>中的元素都是同一个类型的,要么是Dog,要么是Cat,但不可能有些元素是Dog,有些元素是Cat。但类型擦除以后,里面的元素都是用Object来表示,就无法保证类型安全了。所以要保证其里面的元素都是同一个类型的,就要在编译器的层次上来保证,不能往List<?>里面添加元素,这样就保证了List<?>里面都是同一个类型的元素。
下面用代码了说明一下:
List<Animal> animalList = new ArrayList<>();
animalList.add(new Animal());
// List<?> 可以引用任意泛型类型的List
listAny = animalList;
// 由于类型擦除,取出来的元素都是Oject类型的。
final Object o2 = listAny.get(0);
// 可以添加null
listAny.add(null);
// 为了保证类型安全,不能添加其他元素
listAny.add(new Object());
// 报错,List<Object>和List<Animal>不是同一类型的。
objectList = animalList;