自限定
自限定將強制泛型當做自己的邊界參數來使用。自限定所做的,就是要求在繼承關系中,像下面這樣使用這個類:
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 }