首先申明个人观点,仅供参考。
抛出观点:
Java泛型不考虑类型继承
验证过程:
首先明确Java泛型在编译之后是类型擦除的,即ArrayList<Integer>和ArrayList<String>编译之后是同一种class
public static void main(String[] args) { List<Integer> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); System.out.println(list1.getClass() == list2.getClass()); } 输出为true
泛型在编译前会有类型检查
public static void main(String[] args) { List<Integer> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); list1.add("aaa"); // 编译不过 }
类型擦除之后会用原始类型来存储对象引用,即ArrayList<Integer>最后的原始类型是Object
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { List<Integer> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); list1.add(1); // list1.add("aaa"); //此处编译不过 list1.getClass().getMethod("add", Object.class).invoke(list1, "aaa"); System.out.println(list1.get(1)); } 最后输出aaa, 这样是可以运行的,说明list1编译之后擦除了类型,类型检查只在编译前执行,list1擦除类型之后使用Object作为原始类型,可以存储String对象
List类型在get元素时会有一次类型强制转换,对应转换后的类型按变量申明的类型
E elementData(int index) { return (E) elementData[index]; } // Object obj = list1.get(1) 这样取list1的元素,list1强转时对应E就是Object // Integer int1 = list1.get(1) 这样取对应的E就是Integer,此时运行会报ClassCastException,因为第二个元素存储的是一个String
翻看对应的字节码可以看的更清楚,截取了部分字节码
public static void main(java.lang.String[]) throws java.lang.NoSuchMethodException, java.lang.reflect.InvocationTargetException, java.lang.IllegalAccessException; Code: 0: new #2 // class java/util/ArrayList 3: dup 4: invokespecial #3 // Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: iconst_1 10: invokestatic #4 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 13: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z 18: pop 19: aload_1 20: invokevirtual #6 // Method java/lang/Object.getClass:()Ljava/lang/Class; 23: ldc #7 // String add 25: iconst_1 26: anewarray #8 // class java/lang/Class 29: dup 30: iconst_0 31: ldc #9 // class java/lang/Object 33: aastore 34: invokevirtual #10 // Method java/lang/Class.getMethod:(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method; 37: aload_1 38: iconst_1 39: anewarray #9 // class java/lang/Object 42: dup 43: iconst_0 44: ldc #11 // String aaa 46: aastore 47: invokevirtual #12 // Method java/lang/reflect/Method.invoke:(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; 50: pop 51: aload_1 52: invokeinterface #13, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 57: astore_2 58: aload_2 59: invokeinterface #14, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z 64: ifeq 87 67: aload_2 68: invokeinterface #15, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 73: checkcast #16 // class java/lang/Integer // 此处会发生类型强转 76: astore_3 77: getstatic #17 // Field java/lang/System.out:Ljava/io/PrintStream; 80: aload_3 81: invokevirtual #18 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 84: goto 58 87: return
说到正题,List<Integer>和List<Object>
public class Generic { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { List<Integer> list1 = new ArrayList<>(); List<Object> list2 = new ArrayList<>(); list1.getClass().getMethod("add", Object.class).invoke(list1, "aaa"); list2.add(111); A a = new A(); a.print(list1); // 编译不过 } } class A { void print(List<Object> lst) { System.out.println(lst.get(0)); } }
此时必须使用通配符才能解决问题
class A { void print(List<? extends Object> lst) { System.out.println(lst.get(0)); } }
这样给我的理解就是泛型是不会考虑类型继承的,虽然Integer是Object的子类,但是List<Integer>和List<String>并不等价.