问题
// 虽然还不是很清楚对应关系怎么做的,但至少常量池里有类型信息在里面
private Map<String, Integer> map = new HashMap<>();
private Map<Integer, Integer> map222 = new HashMap<>();
private List<Double> list = new ArrayList<>();
#20 = Utf8 map
#21 = Utf8 Ljava/util/Map;
#22 = Utf8 Signature
#23 = Utf8 Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>;
#24 = Utf8 map222
#25 = Utf8 Ljava/util/Map<Ljava/lang/Integer;Ljava/lang/Integer;>;
#26 = Utf8 list
#27 = Utf8 Ljava/util/List;
#28 = Utf8 Ljava/util/List<Ljava/lang/Double;>;
// 无法通过编译
public List<Integer> test(List<String> t) {
return new ArrayList<>();
}
public List<Integer> test(List<Integer> t) {
return new ArrayList<>();
}
概念本质
- 伪泛型
- 类型推断
- 相同或仅一处,直接推断
- 不同,取最小公共父类型,直到Object
- 有返回值,以返回值确定
- 推断传递,即已存在明确的<T>,如Vector<String>
- 类型擦除 -> Object -> 类型推断 -> 强转进行类型替换
特点
- 类型参数不可继承,以下都会编译错误,下面阐述为什么错误
// 大转小
ArrayList<String> list1 = new ArrayList<Object>();
// 拆解
ArrayList<Object> arrayList1 = new ArrayList<Object>();
arrayList1.add(new Object());
arrayList1.add(new Object());
// arrayList2.get()时发生Object到String的强转,故以下语句不合法
ArrayList<String> arrayList2 = arrayList1;
//小转大
ArrayList<Object> list1 = new ArrayList<String>();
// 拆解
ArrayList<String> arrayList1 = new ArrayList<String>();
arrayList1.add(new String());
arrayList1.add(new String());
// 违背泛型设计初衷
ArrayList<Object> arrayList2 = arrayList1;
- 既然存在类型擦除,使用时又不需强转,那么隐式自动强转在哪实现?
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
- 类型擦除与方法覆盖的矛盾
- JVM如何在类型擦除的前提下,使得下面的Override生效
- 解决:桥方法
public class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
class DatePair extends Pair<Date> {
@Override
public Date getValue() {
return super.getValue();
}
@Override
public void setValue(Date value) {
super.setValue(value);
}
}
public java.lang.Object getValue();
descriptor: ()Ljava/lang/Object;
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #6 // Method getValue:()Ljava/util/Date;
4: areturn
LineNumberTable:
line 14: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/fjh/test/genericity/DateInter;
public java.util.Date getValue();
descriptor: ()Ljava/util/Date;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #2 // Method com/fjh/test/genericity/Pair.getValue:()Ljava/lang/Object;
4: checkcast #3 // class java/util/Date
7: areturn
LineNumberTable:
line 17: 0
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this Lcom/fjh/test/genericity/DateInter;
public void setValue(java.lang.Object);
descriptor: (Ljava/lang/Object;)V
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: checkcast #3 // class java/util/Date
5: invokevirtual #5 // Method setValue:(Ljava/util/Date;)V
8: return
LineNumberTable:
line 14: 0
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/fjh/test/genericity/DateInter;
public void setValue(java.util.Date);
descriptor: (Ljava/util/Date;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokespecial #4 // Method com/fjh/test/genericity/Pair.setValue:(Ljava/lang/Object;)V
5: return
LineNumberTable:
line 21: 0
line 22: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/fjh/test/genericity/DateInter;
0 6 1 value Ljava/util/Date;
// 桥方法是给p用的,而不是给dp用的;主要就是实现正确的覆盖
DatePair dp = new DateInter();
dp.set(new Date());
Pair<Date> p = dt;
p.set(new Date());
逆变与协变
概述
- String继承自Object,记做String ≦ Object
- Object[],是由Object构造出来的一种新类型,可以认为是一种构造类型,记f(Object)
- 协变和逆变
- 当A ≦ B时,如果有f(A) ≦ f(B),那么f叫做协变
- 当A ≦ B时,如果有f(B) ≦ f(A),那么f叫做逆变
- 如果都不成立,叫不可变
- 协变
// list1和list2只能添加null
List<? extends Number> list1 = new ArrayList<Integer>();
List<? extends Number> list2 = new ArrayList<Float>();
List<? super Number> list1 = new ArrayList<Number>();
List<? super Number> list2 = new ArrayList<Object>();
list1.add(new Integer(3));
list2.add(new Integer(3));
- PECS
- producer-extends, consumer-super. 即extends只能生产,super只能消费
- 例子:java.util.Collections.copy();
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
int srcSize = src.size();
if (srcSize > dest.size())
throw new IndexOutOfBoundsException("Source does not fit in dest");
if (srcSize < COPY_THRESHOLD || (src instanceof RandomAccess && dest instanceof RandomAccess)) {
for (int i=0; i<srcSize; i++)
dest.set(i, src.get(i));
} else {
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
for (int i=0; i<srcSize; i++) {
di.next();
di.set(si.next());
}
}
}