对于如
				List<E>
				、
				List< String >
				、
				List
				,其中
				List<E>
				称为
				parameterized type
				,
				E
				称为
				(formal) type parameter
				,
				String
				称为
				actual type argument
				,
				List
				称为
				raw type
				。
		
		
				
						Generic
				
				的逻辑意义
		
		
				
						原有
						java
						的类型系统
				
		
		
				Generic
				为
				java 5
				带来了新的类型,这使得
				java
				中的类型关系变得更加复杂,要弄清楚加入了
				generic
				后的类型关系就需要先弄清楚原先
				java
				中的类型系统。
		
		
				首先,在类型的定义上,类之间不允许多重继承,类可以实现多个接口。
		
		
				其次,在类型的使用上,每个变量都必须有明确的类型,变量只能指向相应类型(或相应类型的子类型)的对象,为了实现这一规则,
				compile time
				会对所有的赋值操作做检测,而
				runtime
				则对所有的显示类型转换做检测。
		
		
				最后,数组作为
				java
				中一个特殊的类型,如果
				B extends A
				,那么
				B[] extends A[]
				,当对数组
				element
				赋值时,
				runtime
				会做检测,类型不符则抛
				ArrayStoreException
				,如下。由于有多重数组的出现,意味着
				java
				的类型系统种有无限种类型。
		
		
				
						
								
										// B extends A
								
						
				
		
		
				
						
								
										A a = new A();
								
						
				
		
		
				
						
								
										A[] array = new B[1];
								
						
				
		
		
				
						
								
										Array[0] = a; // ArrayStoreException
								
						
				
		
		
				
						我认为理想状态下的
						generic
				
		
		
				首先,假设有
				B<T> extends A
				,那么
				B<Object> extends A
				、
				B<String> extends B<Object>
				,并且
				runtime
				对使用到
				parameter type
				的输入参数做类型检测。这跟原先
				java
				类型系统中的
				array
				是一致的。与数组相同的还有,因为有如
				B<B<String>>
				、
				B< B<B<String>>>
				等等类型的存在,
				generic
				也可以无限增加可用类型。
		
		
				其次,当
				generic
				跟继承连用时,(在不考虑接口的情况下)有三种新的形式:
				B<T> extends A
				、
				B extends A<String>
				、
				B<T> extends A<T>
				,其中第三种情况意味着有
				B<String> extend A<String>
				。
		
		
				
						现实中的
						generic
				
		
		
				
						       
				
				事实上,在
				java 5
				中,对于
				B<T> extends A
				,
				B<Object>
				跟
				B<String>
				之间并不存在继承关系(
				invariant subtyping
				),这跟数组(
				covariant subtyping
				)不同。之所以使用这种做法,我想有以下原因:
		
		
				首先,
				java 5 compiler
				使用
				erasure
				来支持
				generic
				,所有与
				generic
				相关的信息都不存在于
				runtime
				(见下文中“
				generic
				的实现”),这就意味着
				runtime
				无法做如下的类型检测,而即便
				runtime
				有条件做类型检测,也势必影响代码的执行效率。
		
		
				ArrayList<String> strList = new ArrayList<String>();
		
		
				ArrayList<Object> objList = strList;
		
		
				objList.add(new Object()); // runtime could not throw exception
		
		
				其次,考虑下面的例子,
				B<T> extends A<T>
				,有
				B<String> extends A<String>
				,如果使用
				covariant subtyping
				,又有
				B<String> extends B<Object>
				,这意味着存在多重继承,而多重继承在
				java
				里面是不被允许的。值得注意的是,尽管数组使用
				covariant subtyping
				,但却不会导致多重继承,因为数组属于系统类型,
				java
				并不允许数组被继承。
		
		
				采用了
				invariant subtyping
				之后,假如有
				A<T>
				,由于
				A<Object>
				不再是其他类型
				A<String>
				、
				A<Integer>
				等类型的父类,
				则无法声明可以指向所有
				A<T>
				类型对象的变量。为了解决这一问题,
				java 1.5
				引入了
				wildcard
				,声明为
				A<?>
				类型的变量可以指向所有
				A<T>
				类型的对象。需要注意的是,
				wildcard
				跟继承是两种不同的关系,继承使类型间呈现树状的关系,类型为
				B
				的变量可以指向的对象类型必须在以
				B
				为根节点的子树中,而类型为
				A<?>
				的变量可以指向的对象类型必须为类型树中
				A<Object>
				或与
				A<Object>
				平行的节点。最后,
				wildcard
				跟继承结合使得
				A<?>
				类型变量能够指向的对象类型必须在以
				A<Object>
				及
				A<Object>
				平行的节点为根的所有子树中。
		
		
				// A<T> extends Object, B extends Object, C extends B, D extends B
		
		
				A<?> a; // instances of A<Object>, A<String>, A<Integer> can be assigned to this variable
		
		
				B b; // instance of B, C, D can be assigned to this variable
		
		
				
						Generic
				
				的实现
		
		
				
						加入了
						generic
						后
						java
						的
						type safe
				
		
		
				
						       
				
				保证
				type safe
				,其实就关键在于确保所有变量所指向的对象的类型必须是正确的。我认为在理想状态下,应该实现以下几点:首先,类型为
				A
				的变量所能指向的对象类型必须在以
				A
				为根节点的子树中;其次,类型为
				wildcard
				的变量,如
				A<?>
				,所能指向的对象类型必须在以
				A<Object>
				及
				A<Object>
				平行的节点为根的所有子树中;最后,所有的显式转换在
				runtime
				必须做类型判定。其中,前两点由
				compiler
				实现,最后一点由
				jvm
				实现,然而事实上,
				java 5
				仅实现了前两点,而决定不在
				runtime
				做检测。
		
		
				
						       Compile time
				下
				generic
				的
				type safe
				主要包括
				generic class
				跟
				generic method
				的
				type safe
				,以下分开讨论。
		
		
				
						Generic class
						的
						type safe
				
		
		
				
						       
				
				假设有以下的类:
		
		
				public class A {};
		
		
				public class B<T> extends A {
		
		
				public T obj;
		
		
				}
		
		
				public class C<T> extends B<T> {
		
		
				public void set(T obj) { this. obj = obj; }
		
		
				public T get() { return obj; }
		
		
				}
		
		
				对于类型为
				C<String>
				的对象,能够指向它的变量的类型有:
				A
				、
				B<String>
				、
				C<String>
				、
				B<?>
				、
				C<?>
				。对于类型为
				A
				的变量,通过该变量无法访问到任何与
				T
				相关的方法或对象变量,很显然在原有
				java
				的
				type safe
				机制仍然有效;对于类型为
				B<String>
				、
				C<String>
				的变量,
				compiler
				对所有通过该变量所访问的方法(
				set
				、
				get
				)或对象变量
				(obj)
				进行检测,所有涉及到
				T
				的赋值都必须满足
				T=String
				,则
				type safe
				得以保证。对于类型为
				B<?>
				、
				C<?>
				的变量,通过该变量所访问的方法或对象变量,所有的输出值中
				T
				类型被替换成
				T
				的
				bound
				(见下文中“
				type parameter
				的限制”),所有输入值中由于
				T
				类型未知,所以不能接受任何变量赋值(
				null
				除外)。在理想状态下,输入值中
				T
				类型应该也被替换成
				T
				的
				bound
				,然后由
				runtime
				去做类型判定,但是由于
				runtime
				没有
				generic
				相关的任何信息
		
		
				C<String> strC = new C<String>();
		
		
				C<?> c = strC;
		
		
				// even if the following code pass compile time check, runtime could not throw exception
		
		
				c.obj = new Object();
		
		
				c.set(new Object());
		
		
				// here’s a unexpected exception
		
		
				String str = strC.obj;
		
		
				str = strC.get();
		
		
				在
				generic class
				的所有方法中,
				T
				的类型被认为是其
				bound
				或者
				bound
				的某个子类。也就是说,首先,
				T
				的变量只能指向类型为
				T 
				或
				T
				的子类的对象;其次,通过
				T
				的变量只能访问到其
				bound
				的方法和对象变量。假设以下代码存在于
				C
				的
				set
				方法中:
		
		
				
						
								
										public void set(T obj;) {
								
						
				
		
		
				
						
								
										Object temp;
								
						
				
		
		
				
						
								
										temp = obj; // ok
								
						
				
		
		
				
						
								
										obj = temp; // ompile error
								
						
				
		
		
				
						
								
										obj.toString(); // can access Object’s methods
								
						
				
		
		
				
						
								
										}
								
						
				
		
		
				
						Generic method
						的
						type safe
				
		
		
				与
				Generic class
				不同的是,在
				generic method
				中,
				actual type argument
				并非指定的,而是由
				compiler
				推断出的(
				Inference
				)。
				Compiler
				通过对
				generic method
				中的输入变量的类型推断
				type parameter
				的类型,如果不能够得到一个
				unique smallest type
				,则被视为
				compile error
				,参考以下代码:
		
		
				public <A> void doublet(A a, A b) {};
		
		
				…
		
		
				// compile error, because String and Integer have both Comparable and Serializable as common supertypes
		
		
				doublet(“abc”, 123); 
		
		
				当
				wildcard
				跟
				generic method
				同时使用时,有以下的特例:
		
		
				public <T> List<T> test(List<T> list) { return list; }
		
		
				…
		
		
				List<?> wildcardList = new ArrayList<String>();
		
		
				wildcardList = test(wildcardList);
		
		
				最后,
				generic method
				中对
				type parameter
				的使用所必须遵循的规则跟上面所提到的
				generic class
				的方法中的规则是一样的。
		
		
				
						Erasure
						的实现方式
				
		
		
				Java 5
				在
				compiler
				中采用
				erasure
				来实现
				generic
				,经过
				erasure
				的处理,所有与
				generic
				相关的信息将被抹掉(
				erase
				),同时在适当的位置插入显式类型转换,最终形成的
				byte code
				跟
				java1.4
				的
				byte code
				没有什么不一样。
		
		
				首先,
				parameterized type
				,被还原成其
				non-parameterized type
				,如
				List<String>
				将变成
				List
				。
		
		
				其次,
				type parameter
				被替换成它的
				bound
				,如
				T
				将变成
				Object
				(假如它的
				upper bound
				是
				Object
				)。
		
		
				接着,对于方法类成员的返回值,如果其类型为
				parameter type
				,
				erasure
				则会插入显式转换。如:
		
		
				public class A<T> {
		
		
				public T get() { return null; }
		
		
				}
		
		
				…
		
		
				A<String> a = new A<String>();
		
		
				String temp = a.get();
		
		
				// translate to
		
		
				public class A {
		
		
				public Object get() { return null; }
		
		
				}
		
		
				…
		
		
				A a = new A();
		
		
				String temp = (String) a.get();
		
		
				最后
				erasure
				将在必要的时候插入
				bridge method
				。对于以下的代码
		
		
				public class A<T> {
		
		
				private T obj;
		
		
				public void set(T obj) { this.obj = obj; }
		
		
				public T get() { return obj; }
		
		
				}
		
		
				public class B extends A<String> {
		
		
				public void set(String obj) {};
		
		
				public String get() { return null;}
		
		
				}
		
		
				…
		
		
				A<String> a = new B();
		
		
				a.set(“abc”);
		
		
				String temp = a.get();
		
		
				在没有
				bridge method
				存在的情况下,对于
				a
				的方法的调用将无法获得多态性的支持,原因是
				B
				中的方法的
				signature
				跟
				A
				的不同,所以不被
				jvm
				视为重载。这时候
				erasure
				必须在
				B
				中插入如下的
				bridge method
				:
		
		
				public void set(Object obj) { set((String) obj);}
		
		
				public Object get() { return get(); }
		
		
				需要注意的是
				get
				的
				bridge method
				在是编译不过的,因为
				java
				不允许这种形式的
				overload
				,事实上,
				bridge method
				是直接在
				byte code
				中插入的。
		
		
				最后值得注意的是,
				bridge method
				只有在需要的时候被插入,如果
				B
				不重载
				get
				跟
				set
				方法,将不会有
				bridge method
				存在。
		
		
				
						由于
						runtime
						缺乏
						generic
						相关的信息而导致的各种限制
				
		
		
				
						1.       
				
				通过
				wildcard
				类型的变量访问方法及对象变量受到限制(如上文所述)。
		
		
				
						2.       
				
				与
				type parameter
				相关的显式转化无法保证
				type safe
				,同时
				compiler
				会有
				warning
				。
		
		
				List<?> list = new ArrayList<String>();
		
		
				List<String> strList = (List<String>) list; // warning
		
		
				public <T> T test1(Object obj) { return (T) obj; } // warning
		
		
				public <T> T[] test2(Object[] objs) { return (T[]) objs; } // warning
		
		
				
						3.       
				
				在创建某些类型对象时受到限制。
		
		
				public <T> T test1(T sample) { return new T(); } // compile error
		
		
				public <T> T[] test2(T sample) { return new T[0]; } // compile error
		
		
				值得注意的是,即便提供了
				actual type argument
				,依然无法创建
				parameterized type
				的数组:
		
		
				// compile error, but assumes that compiler allow to create such kind of array
		
		
				List<String>[] lists = new List<String>[1];
		
		
				List<Integer> intList = new List<Integer>();
		
		
				intList.add(1);
		
		
				Object[] objs = lists;
		
		
				objs[0] = intList; // runtime could not throw an ArrayStoreException for this
		
		
				String temp = lists[0].get(0); // unexpected error
		
		
				通过
				Class<T>
				能够创建
				T
				的对象,
				Class<T>
				的奥妙在于,一方面它能够通过
				compiler
				的检测,另一方面,它本身携带的信息也足以让
				runtime
				得以创建
				T
				的对象。
		
		
				public T create(Class<T> c) { return c.newInstance(); };
		
		
				…
		
		
				String temp = create(String.class);
		
		
				
						4.       
				
				不得不插入
				bridge method
				(如上文所述)。
		
		
				
						5.       
				
				使用
				instanceof
				时受到限制。
		
		
				List<String> list = new ArrayList<String>();
		
		
				boolean temp;
		
		
				temp = list instanceof List<String>; // compile error
		
		
				temp = list instanceof List<?>; // ok
		
		
				
						6.       
				
				使用
				reflection
				时存在安全隐患。
		
		
				public class A<T> {
		
		
				public T obj;
		
		
				}
		
		
				public class B {
		
		
				public A<String> a;
		
		
				}
		
		
				…
		
		
				A<Integer> a = new A<Integer>();
		
		
				B b = new B();
		
		
				B.class.getField(“a”).set(a); // everything ok
		
		
				String temp = b.a.obj; // unexpected error
		
		
				
						7.       
				
				Generic class
				中的
				type parameter
				在其静态方法及静态变量中无法使用。
		
		
				在
				
						generic
				
				中对
				
						type parameter
				
				的限制
		
		
				
						使用
						extends
						关键字
				
		
		
				对于如
				A<T>
				的
				generic class
				,可以使用
				extends
				来进一步限制
				T
				所能代表的类型。如:
		
		
				public class A<T extends Number> { … }
		
		
				…
		
		
				A<Object> objA; // compile error
		
		
				A<String> strA; // compile error
		
		
				A<Number> numA; // ok
		
		
				A<Integer> intA; // ok
		
		
				这里,
				extends
				意味着
				T
				必须是
				Number
				或者
				Number
				的子类,以下是对于
				extends
				更为复杂的使用。
		
		
				public class B<T extends B<T>> {…}
		
		
				public class C extends B<C> { … }
		
		
				public class D extends C { … }
		
		
				…
		
		
				C c = new C();
		
		
				D d = new D();
		
		
				B<C> bc; // ok
		
		
				bc = c;
		
		
				bc = d;
		
		
				B<D> bd; // compile error
		
		
				B<Object> b; // compile error
		
		
				这里,显然
				B<Object>
				是非法的,对于
				B<D>
				,虽然
				D
				继承了
				C
				,但是把
				D
				替换到“
				T extends B<T>
				”中,显然“
				D extends B<D>
				”不成立,所以
				B<D>
				也是非法的,与此类似的是
				java.lang
				里面的“
				Enum<E extends Enum<E>>
				”,这一声明保证了,假设有类
				Test
				,它不是
				Enum
				类型(编译器保证只有使用
				enum
				关键字创建时才能满足
				Enum
				类中对
				T
				的限制),那么无法声明
				Enum<Test>
				类型的变量。
		
		
				public class E<T, S extends T> { … }
		
		
				…
		
		
				E<Number, Number> e1; // ok
		
		
				E<Number, Integer> e2; // ok
		
		
				E<Number, String> e3; // compile error
		
		
				这里,
				extends
				用来限制不同的
				type parameter(T
				、
				S)
				之间的关系。
		
		
				最后,需要注意的是,在
				generic class
				里面通过使用
				extends
				对
				type parameter
				进行限制所导致的结果是删除了一部分类型(如上述的
				E<Number,String>
				),而并非阻止这一类型的对象的创建,而在
				generic method
				里面使用
				extends
				则在于阻止某些类型的对象作为输入参数,如:
		
		
				public <T extends String> void test(List<T> list) {}
		
		
				…
		
		
				test(new ArrayList<String>()); // ok
		
		
				test(new ArrayList<Object>()); // compile error
		
		
				
						关于
						wildcard – “?”
				
		
		
				在不使用
				super
				关键字的时候,
				”?”
				可以理解成
				parameter type
				的匿名形式,事实上,这些
				”?”
				都可以转变成用
				parameter type
				表示。
		
		
				
						
								
										public void test(List<? extends String> list) {}
								
						
				
		
		
				
						
								
										public <T extends String> void test(List<T> list) {}
								
						
				
		
		
				“?”
				只能作为
				parameterized type
				的
				actual type argument
				使用,同时由于
				”?”
				是匿名的形式,编译器并不会认为出现两次的
				A<?>
				要求相同的
				type parameter
				。
		
		
				public void test(? obj) {} // compile error
		
		
				public <T> void test1(List<T> list1, List<T> list2) {}
		
		
				public void test2(List<?> list1, List<?> list2) {}
		
		
				…
		
		
				test1(new ArrayList<Object>(), new ArrayList<String>()); // compile error
		
		
				test2(new ArrayList<Object>(), new ArrayList<String>()); // ok
		
		
				
						使用
						super
						关键字
				
		
		
				对于
				T super A
				,
				super
				表示
				T
				必须是
				A
				或者
				A
				的父类,相比起
				extends
				,对
				super
				的使用有更多的限制。考虑以下代码:
		
		
				public <T super Number> void test(T obj) {} // assumes that there’s no compile error
		
		
				…
		
		
				test(new Object()); // this looks reasonable
		
		
				test(1); // Integer is not super class of Number, so compiler should reject this, but Integer is also an object, why would a method accept object as valid argument but not integer?
		
		
				可以看到,
				super
				限制对象类型必须是某个类或其父类,而继承则允许父类的变量接受子类的对象,这两者是互相抵触的,所以对
				super
				的时候有以下的限制:
		
		
				首先,
				super
				只能在
				parameterized type
				的
				actual type argument
				中作为限制条件使用,在这个时候,它并不和继承相抵触。如:
		
		
				public void test(List<? super Number> list) {}
		
		
				其次,
				super
				不能用于限制非匿名的
				parameter type
				,显然如果可以这样的话,就会出现上述代码中的错误。这就决定了
				super
				只能与
				”?”
				连用。
		
		
				
						Generic
				
				的向前兼容
		
		
				下面以
				List<E>
				为例,为了向前兼容原先的代码,允许使用
				List
				这样的
				raw type
				。在语义上,
				List
				跟
				List<Object>
				是一致的,然而在语法上
				List
				跟
				List<?>
				更为类似,并且编译器允许原先在
				List
				使用原先在
				List<?>
				上禁止的某些操作(对于
				List<?>
				为
				compile error
				的操作在
				List
				上仅仅是
				warning
				)。
		
		
				首先,在类型转换上,
				List
				跟
				List<?>
				是等价的,
				List
				类型可以赋给任何指定了
				actual type argument
				的
				parameterized type
				(如
				List<String>
				),而
				List<?>
				则不行(除非使用显式转换)。
		
		
				List list;
		
		
				List<?> wildcardList;
		
		
				List<String> strList;
		
		
				
						 
				
		
		
				list = wildcardList;
		
		
				list = strList;
		
		
				wildcardList = list;
		
		
				wildcardList = strList;
		
		
				
						 
				
		
		
				strList = list; // warning
		
		
				strList = wildcardList; // compile error
		
		
				其次,
				compiler
				不允许通过
				List<?>
				访问任何输入参数与
				E
				相关的方法,而
				List
				则仅给出
				wanring
				。
		
		
				list.add(“abc”); // waring
		
		
				wildcardList.add(“abc”); // compile error
		
		
				其他
		
		
				
						关于
						GJ
				
		
		
				GJ
				是使
				java
				支持
				generic
				的一个开源项目,
				java 5
				的
				generic
				是参照
				GJ
				实现的,
				GJ
				的语法以及实现方式基本上跟目前
				java 5
				的
				generic
				相同,当然在某些细节方面
				java 5
				的
				generic
				做了改动,研究
				GJ
				有利于更好的理解
				java 5
				的
				generic
				。
		
		
				
						Security Implications
				
		
		
				考虑以下的代码:
		
		
				public class SecureChannel extends Channel {
		
		
				public String read();
		
		
				}
		
		
				public class A {
		
		
				public LinkedList<SecureChannel> cs;
		
		
				…
		
		
				}
		
		
				由于
				LinkedList<SecureChannel>
				在
				runtime
				会退化成
				LinkedList
				,恶意的代码很容易在
				cs
				里面放入其他类型的
				Channel
				。
				GJ
				中建议使用
				type specialization
				来解决这一问题,但是由于
				java 5
				的
				generic
				仅在需要的时候插入
				bridge
				方法,所以这一方法在
				java 5
				中是无效的。
		
		
				
						在方法参数中使用
						extends
						、
						super
						关键字的技巧
				
		
		
				考虑如下代码:
		
		
				public class A<T> {
		
		
				private T obj;
		
		
				public void set(T obj) { this.obj = obj; }
		
		
				public T get() { return obj; }
		
		
				}
		
		
				…
		
		
				public class Test<T> {
		
		
				public void test(A<T> a) { … }
		
		
				…
		
		
				}
		
		
				…
		
		
				A<Object> a = new A<Object>();
		
		
				A<Number> numA = new A<Number>();
		
		
				A<Integer> intA = new A<Integer>();
		
		
				Test<Number> test = new Test<Number>();
		
		
				test.test(a); // compile error (1)
		
		
				test.test(numA); // ok
		
		
				test.test(intA); // compile error (2)
		
		
				对于
				test
				方法,如果方法内仅需要调用
				A<T>
				的
				set
				方法(即仅需要用到输入值为
				T
				类型的方法,注意,这不包括如
				List<T>
				这种类新),使用
				A<? super T>
				代替
				A<T>
				可能会更为合适,这使得
				(1)
				得以编译通过,由于仅需要调用
				A<T>
				的
				set
				方法,
				A<Object>.set(Object)
				显然比
				A<Number>.set(Number)
				允许更多的类型,从而使
				A<Object>
				可以替换
				A<Number>
				。相似的,如果
				test
				方法内仅需要调用
				A<T>
				个
				get
				方法,则使用
				A<? extends T>
				代替
				A<T>
				可能会更合适,这使得
				(2)
				得以编译通过。
		
		
				
						关于
						type parameter
						的命名规范
				
		
		
				推荐使用精简同时有意义的名称,如
				E for element
				、
				T for type
				(最好是单个字母),同时避免使用任何小写字母以使得
				type parameter
				能够从一般的类还有接口名称中被区分出来。如果需要同时使用多个
				type parameter
				,则考虑使用邻近的几个不同字母,如
				T
				、
				S
				。如果在某个类中已经使用了某个字母作为
				type parameter
				,则在其
				generic method
				以及
				nested class
				中避免使用同样的字母。
		
		
				
						Generic method
						采用
						inference
						所产生的问题
				
		
		
				public interface I {}
		
		
				public class A implements I {}
		
		
				public class B{}
		
		
				public <T> void test(T a, T b);
		
		
				…
		
		
				test(new A(), new B()); // ok
		
		
				当代码改动
				B
				也需要实现接口
				I
				的时候:
		
		
				public class B implements I {}
		
		
				…
		
		
				test(new A(), new B()); // compile error
		
		
				
						仍然搞不懂的地方
				
		
		
				对于类似
				java.util.Collections
				的
				max
				方法,经过我的试验以下两种声明方式所能接受的类型是一样的,不明白它为什么要用前者。
		
		
				public static <T extends Object & Comparable<? super T>> T max1(Collection<? extends T> coll)
		
		
				public static <T extends Object & Comparable<? super T>> T max2(Collection<T> coll)
		
		
				
						参考资料
				
		
		
				GJ- Making the future safe for the past: Adding Genericity to the JavaTM Programming Language
		
		
				Generics in the Java Programming Language