假设 B extends A,我们调用calc(new C())的时候,父类型的相关方法总是会被擦除到 bounding type(假定 C 的擦除类型是 Object),所以子类的方法也带有这个 bounding type 的方法实现calc(Object object),但为了保持多态,编译器又生成了一个calc(C c)(相当于做了一次重载),真正要使用多态,就必须产生一个 synthesized bridge method,执行calc(Object object) {calc((C)object);}。这个东西是为了保持多态(preserve polymorphism)。泛型集成对比点 1。
为了兼容性(compatibility)考虑,A a = new A<String>() 之类的赋值(从泛型到擦除类型的赋值总是会成立的)总是会成立,但编译器总是会报 warning。猫插入狗列表中问题,只有在真实地 set 和 get 操作时才会发生。在 Jackson 中经常遇见如下问题:实际反序列化的生成 class 是 LinkedHashMap,但 Entity> = JsonUtil.toObject(str)等还是会赋值成功(此处 实际得到的是 A)。
泛型不能做什么
所有泛型的 bug 都与一个基本假设有关,在虚拟机里运行的类型实际上使用的是 raw type,不注意这一点就可能出错。parameterized type vs raw type。
不能使用基本类型实例化 type parameters。
动态类型识别只能在 raw type 上工作,也就是 instanceof、Class ==、getClass() 这类操作都只能当做 raw type 工作。
不能创建 parameterized type 的数组(Pair<Sring>[] a = new Pair<String>[1]是不可能的) - 但可以赋值到泛型数组,见下一条。
泛型类的 type variables 不能在它的 static 上下文里工作。但静态方法自己可以有 type variables。-各有各的泛型。
不能抛出和捕获异常类。
巧妙地使用泛型,可以破坏 checked exception 的限制
因为每个泛型方法都有一个兜底的 raw type 方法兜底,如果兜底方法和父类的非泛型方法相冲突(clash),编译会报错。举例,永远不要写boolean equals(T object),因为编译器会擦除出 boolean equals(Object object),制造同签名的方法,连重载都算不上。
泛型能够做什么
type variable 可以拿来做目标参数,如T t = get()和(T)。
通配符(wildcard)
?是通配符。 ? extends T 作为一个 type parameter 证明可以在此处读。Pair<? extends Employee>意味着它的子类 可以是 Pair 和 Pair。 ? super T 作为一个 type parameter 证明可以在此处写。Pair<? super Employee>意味着它的子类 可以是 Pair 和 Pair
LocalDate是ChronoLocalDate的子类(顺序就是这样,没有反过来),但 ChronoLocalDate 已然实现了 comparable<ChronoLocalDate>。这时候 LocalDate 的比较方法就应该声明成<T extends Comparable<? super T>>- 这是 comparable 的标准泛型方案。从 A 派生出 B,则 B 的 comparable 方法必须声明为可以支持 super 的类型,这样对 A 的 compare 才能同时兼容 A、B - 而不只是 B,Lists 的 removeIf 方法的谓词同理。(泛型集成对比点 2)
/** * This constructor must be invoked from an anonymous subclass * as new TypeLiteral<. . .>(){}. */ publicTypeLiteral() { // TypeparentType= getClass().getGenericSuperclass(); if (parentType instanceof ParameterizedType) { // 重要:这里得到的 Type 是真的 Class 实例,不过做类型判定不能用 instanceof,只能用 assignableFrom。因为 Class 的自身类型和指代类型是不一样的。当然 Type 也可能是 ParameterizedType 等任意子类型,它们也可以通过 getClass 理解自身的类型。 type = ((ParameterizedType) parentType).getActualTypeArguments()[0]; } else thrownewUnsupportedOperationException( "Construct as new TypeLiteral<. . .>(){}"); }
publicabstractclassTypeReference<T> implementsComparable<TypeReference<T>> { protectedfinal Type _type;
protectedTypeReference() { TypesuperClass= getClass().getGenericSuperclass(); if (superClass instanceof Class<?>) { // sanity check, should never happen thrownewIllegalArgumentException("Internal error: TypeReference constructed without actual type information"); } /* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect * it is possible to make it fail? * But let's deal with specific * case when we know an actual use case, and thereby suitable * workarounds for valid case(s) and/or error to throw * on invalid one(s). */ _type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; } }