Java泛型(8):自限定&参数协变
自限定
自限定将强制泛型当做自己的边界参数来使用。自限定所做的,就是要求在继承关系中,像下面这样使用这个类:
class A extends SelfBounded<A> {}
它的意义是可以保证类型参数必须与正在被定义的类相同。自限定只能强制作用于继承关系。如果使用自限定,就应该了解这个类所用的类型参数将与使用这个参数的类具有相同的基本类型。
下面是一个自限定的例子【1】:
1 class SelfBounded<T extends SelfBounded<T>> { 2 T element; 3 SelfBounded<T> set(T arg) { 4 element = arg; 5 return this; 6 } 7 T get() { return element; } 8 } 9 10 class A extends SelfBounded<A> {} 11 class B extends SelfBounded<A> {} // It's OK. 12 13 class C extends SelfBounded<C> { 14 C setAndGet(C arg) { set(arg); return get(); } 15 } 16 17 class D {} 18 // class E extends SelfBounded<D> {} // [Compile error]: Type parameter D is not within its bound 19 20 public class SelfBounding { 21 public static void main(String[] args) { 22 A a = new A(); 23 a.set(new A()); 24 a = a.set(new A()).get(); 25 a = a.get(); 26 C c = new C(); 27 c = c.setAndGet(new C()); 28 } 29 }
我们发现class E是不能编译的。如果移除自限定这个限制(class SelfBounded<T>),这样E就可以编译了。但是就不能限制E这样的非自限定类型继承SelfBounded类了。
参数协变
先看一个协变返回类型的例子【2】:
1 class Base {} 2 class Derived extends Base {} 3 4 interface OrdinaryGetter { 5 Base get(); 6 } 7 8 interface DerivedGetter extends OrdinaryGetter { 9 // DerivedGetter.get()覆盖了OrdinaryGetter.get() 10 @Override Derived get(); 11 } 12 13 public class CovariantReturnTypes { 14 void test(DerivedGetter d) { 15 Derived result1 = d.get(); // 调用的DerivedGetter.get() 16 Base result2 = d.get(); // 也调用的DerivedGetter.get() 17 } 18 }
而自限定泛型将产生确切的导出类型作为其返回值。请看例【3】:
1 interface GenericGetter<T extends GenericGetter<T>> { 2 T get(); 3 } 4 5 interface Getter extends GenericGetter<Getter> {} 6 7 public class GenericsAndReturnTypes { 8 void test(Getter g) { 9 Getter result1 = g.get(); 10 GenericGetter result2 = g.get(); // Also the base type 11 } 12 }
例【2】可以证明,返回值并不是区分两个不同方法的途径。而下面的例【4】则说明参数类型可以区分两个不同的方法。所以例【2】是覆盖(override)而例【4】是重载(overload)。
1 class OrdinarySetter { 2 void set(Base base) { 3 System.out.println("OrdinarySetter.set(Base)"); 4 } 5 } 6 7 class DerivedSetter extends OrdinarySetter { 8 // @Override // [Compile Error]: Can't override. It's overload not override! 9 void set(Derived derived) { 10 System.out.println("DerivedSetter.set(Derived)"); 11 } 12 } 13 14 // 在非泛型代码中,参数类型不能随子类型发生变化。 15 public class OrdinaryArguments { 16 public static void main(String[] args) { 17 Base base = new Base(); 18 Derived derived = new Derived(); 19 DerivedSetter ds = new DerivedSetter(); 20 ds.set(derived); // 调用DerivedSetter的set 21 ds.set(base); // 调用OrdinarySetter的set 22 } 23 }
但是,在使用自限定类型时,在导出类中只有一个方法,并且这个方法接受导出类型而不是基类型作为参数。例子【5】:
1 interface SelfBoundSetter<T extends SelfBoundSetter<T>> { 2 void set(T arg); 3 } 4 5 interface Setter extends SelfBoundSetter<Setter> {} 6 7 public class SelfBoundingAndCovariantArguments { 8 void testA(Setter s1, Setter s2, SelfBoundSetter sb1, SelfBoundSetter sb2) { 9 s1.set(s2); 10 // 编译器不能识别将基类型当做参数传递给set的尝试,因为没有任何方法具有这样的签名。事实上,这个参数已经被覆盖。 11 // s1.set(sb1); // [Compile Error]: set(Setter) in SelfBoundSetter<Setter> cannot be applied to (SelfBoundSetter) 12 sb1.set(s1); 13 sb1.set(sb2); 14 } 15 }
如果上例不使用自限定类型,普通继承机制就会介入,而你将能够重载,就像在非泛型的情况下一样(自限定类型可以避免这种情况发生):
1 class GenericSetter<T> { // Not self-bounded 2 void set(T arg){ 3 System.out.println("GenericSetter.set(Base)"); 4 } 5 } 6 7 class DerivedGS extends GenericSetter<Base> { 8 // @Override // [Compile Error]: Can't override. It's overload not override. 9 void set(Derived derived){ 10 System.out.println("DerivedGS.set(Derived)"); 11 } 12 } 13 14 public class PlainGenericInheritance { 15 public static void main(String[] args) { 16 Base base = new Base(); 17 Derived derived = new Derived(); 18 DerivedGS dgs = new DerivedGS(); 19 dgs.set(derived); 20 dgs.set(base); // Compiles: overloaded, not overridden! 21 } 22 }