Java编程思想笔记0x08

泛型(三)

问题

任何基本类型都不能作为类型参数

  • Java泛型中不能使用基本类型用作类型参数,取而代之可以使用基本类型的包装器以及自动包装机制。

实现参数化接口

  • 一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。

转型和警告

  • 使用带有泛型类型参数的转型或instanceof不会有任何效果。因为类型参数T会被擦除到第一个边界,默认为Object,使用转型或instanceof也只使用了Object
  • 在必须进行转型到某种泛型类时,需要使用泛型类对象转型,例如List.class.cast(x),但是无法转换到具体类型,即不能使用List<RealClass>.class.cast(x)

重载

  • 由于擦除的原因,相同泛型类、不同类型参数的重载方法将产生相同的类型签名。

基类劫持接口

  • 当父类实现泛型接口时,实现了接口中的某个带有相应泛型参数的方法,此时子类将不能修改该方法的参数类型。

自限定的类型

古怪的循环泛型

  • 诸如class Sub extends Basic<Sub>的定义就是古怪的循环泛型,子类类型出现在了基类中。
  • 其本质是基类用导出类替代其参数,意味着泛型基类变成了一种其所有导出类的公共功能的模板,但是这些功能对于其所有的参数和返回值,将使用导出类型。

自限定

  • 下面代码中SelfBounded即自限定的泛型基类。
class SelfBounded<T extends SelfBounded<T>> {
    T element;
    SelfBounded<T> set(T arg) {
        element = arg;
        return this;
    }
    T get() {
        return element;
    }
}

继承SelfBounded必须类似与class A extends SelfBounded<A>来定义子类,这保证了类型参数必定与正在被定义的类相同。

同样的,自限定还可以用于泛型方法,防止该方法用于自限定参数之外的任何事物上:

public class SelfBoundingMethods {
    static <T extends SelfBoundingMethods<T>> T f (T arg) {
        return arg.set(arg).get();
    }
    public static void main(String[] args) {
        A a = f(new A());
    }
}

参数协变

  • 自限定类型的意义在于能够产生协变参数类型,即方法参数类型会随着子类变化而变化,不会出现重载。

但实际上如果仅为了实现参数协变,自限定并不是必要的,使用循环泛型就能解决,例如:

public class Test {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(a.set(a));
        System.out.println(a);
        System.out.println(a.get());
        B b = new B();
        System.out.println(b.set(b));
        System.out.println(b);
        System.out.println(b.get());
    }
}
class SelfBounded<T extends SelfBounded<T>> {
    T element;
    SelfBounded<T> set(T arg) {
        element = arg;
        return this;
    }
    T get() {
        return element;
    }
}
class Cyclic<T> {
    T element;
    Cyclic<T> set(T arg) {
        element = arg;
        return this;
    }
    T get() {
        return element;
    }
}
class A extends SelfBounded<A> {}
class B extends Cyclic<B> {}
/* Output:
A@2c13da15
A@2c13da15
A@2c13da15
B@9e89d68
B@9e89d68
B@9e89d68
*/

上面代码中,SelfBounded是自限定的,而Cyclic仅是普通的泛型类,其子类B使用了循环泛型就实现了参数协变。两者唯一的不同仅在于自限定中类型参数必须是自限定的,而循环泛型并无此限制

Author: SinLapis
Link: http://sinlapis.github.io/2019/07/05/Java编程思想笔记0x08/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.