Java集合与泛型中的陷阱

java

List,List<Object>区别

  List<Integer> t1 = new ArrayList<>();

// 编译通过

List t2 = t1;

//编译失败

List<Object> t3 = t1;

t1 可以赋给 t2, 但是 t1 不能赋给 t3,会抛出如下异常

Error:(16, 35) java: 不兼容的类型: java.util.List<java.lang.Integer>无法转换为java.util.List<java.lang.Object>

List<?>注意点

	List<Object> t1 = new ArrayList<>();

List<?> t2 = t1;

// 编译通过

t2.remove(0);

t2.clear();

// 编译不通过

t2.add(new Object());

List<?> 是一个泛型,在没有赋值之前,是可以接受任何集合的赋值的,但是请注意,赋值之后就不能往里面添加元素了

提示如下错误:

Error:(18, 19) java: 对于add(java.lang.Object), 找不到合适的方法

方法 java.util.Collection.add(capture#1, 共 ?)不适用

(参数不匹配; java.lang.Object无法转换为capture#1, 共 ?)

方法 java.util.List.add(capture#1, 共 ?)不适用

(参数不匹配; java.lang.Object无法转换为capture#1, 共 ?)

所以 List<?> 一般用来作为参数来接受外部的集合,或者返回一个不知道具体元素的集合。

<? extends T> 与 <? super T>

<? extends T>

<? extends T> a,a 这个变量可以接受 T 及其 T 子类的集合,上界为 T,并且从 a 取出来的类型都会被强制转换为 T

class Animal{

}

class Cat extends Animal{

}

class RedCat extends Cat{

}

demo:

	    List<Animal> animals = new ArrayList<>();

List<Cat> cats = new ArrayList<>();

List<RedCat> redCats = new ArrayList<>();

// 可以通过编译

List<? extends Cat> extendsCat = redCats;

// 不能通过编译,因为只能接受 Cat 及其子类的集合

extendsCat = animals;

// 重点注意:下面三行都不能通过编译

extendsCat.add(new Animal());

extendsCat.add(new Cat());

extendsCat.add(new RedCat());

// 重点注意:可以通过编译

extendsCat.add(null);

<? extends T>最需要注意的是,就是不能向里面添加除null之外的其他所有元素,这个和 List<?> 有点类似

<? super T>

<? super T>,它和 <? extends T> 有点相反。**对于 <? super T> a**,a 这个变量可以接受 T 及其 T 父类的集合,下界为 T,并且从 a 取出来的类型都会被强制转换为 Object

demo:

    List<Animal> animals = new ArrayList<>();

List<Cat> cats = new ArrayList<>();

List<RedCat> redCats = new ArrayList<>();

// 可以通过编译

List<? super Cat> superCat = animals;

// 不能通过编译,因为只能接受 Cat 及其父类的集合

superCat = redCats;

// 重点注意:不能通过编译,只能添加 Cat 及其 Cat 的子类

superCat.add(new Animal());

// 重点注意,可以通过编译

superCat.add(new Cat());

superCat.add(new RedCat());

superCat.add(null);

注意,<? super T>最需要注意的是,在虽然可以接受 T 及其父类的赋值,但是只能向里面添加 T 及其 T 的子类。

总结

1、List<? extends T> a ,可以把 a 及其 a 的子类赋给 a,从 a 里取的元素都会被强制转换为 T 类型,不过需要注意的是,不能向 a 添加任何除 null 外是元素。

2、List<? super T> a ,可以把 a 及其 a 的父类赋给 a,从 a 里取的元素都会被强制转换为 Object 类型,不过需要注意的是,可以向 a 添加元素,但添加的只能是 T 及其子类元素。

List泛型与重载

你觉得下面这道题能够编译通过吗?

class GernerTypes {

public static void method(List<Integer> list) {

System.out.println("List<Integer> list");

}

public static void method(List<String> list) {

System.out.println("List<String> list");

}

}

不通过编译,错误信息:

Error:(20, 28) java: 名称冲突: method(java.util.List<java.lang.String>)和method(java.util.List<java.lang.Integer>)具有相同疑符

两个方法的参数不同,为什么会重载不通过呢?

实际上在 Java 的泛型中,泛型只存在于源码中,在编译后的字节码中,泛型已经被替换为原生类型了,并且在相应的地方插入了强制转换的代码。

所以上面的两个方法,看似参数不一样,但是经过编译擦出之后,他们的参数就是一样的了,所以编译不通过。

数组集合转换:

String[] arr = {"one", "two", "three"};

// 数组转换成集合

List<String> list = Arrays.asList(arr);

// 向集合添加元素:编译正常,但运行时抛出了异常

list.add("four");

运行时报错:

Exception in thread "main" java.lang.UnsupportedOperationException

at java.util.AbstractList.add(AbstractList.java:148)

at java.util.AbstractList.add(AbstractList.java:108)

at com.qhong.basic.list.test.main(test.java:16)

问题来了,向集合添加元素为啥会抛出异常呢??

我们先来看一下 Arrays.asList(arr) 方法究竟返回了什么?

看源码:

 public static <T> List<T> asList(T... a) {

return new ArrayList<>(a);

}

/**

* @serial include

*/

private static class ArrayList<E> extends AbstractList<E>

implements RandomAccess, java.io.Serializable

{

private static final long serialVersionUID = -2764017481108945198L;

private final E[] a;

ArrayList(E[] array) {

a = Objects.requireNonNull(array);

}

。。。。

返回的List内部直接引用了原数组arr

原数组长度固定为3,所以不可以再add

下面的代码证明这一点

String[] arr = {"one", "two", "three"};

// 数组转换成集合

List<String> list = Arrays.asList(arr);

// 修改 arr

arr[0] = "0";

//打印看看

System.out.println(list.get(0));

打印:

0

建议大家这样转换比较安全

List<String> list = new ArrayList<>(Arrays.asList(arr));

参考:

Java集合与泛型中的几个陷阱,你掉进了几个?

以上是 Java集合与泛型中的陷阱 的全部内容, 来源链接: utcz.com/z/394019.html

回到顶部