Generic 泛型

Generic 泛型 -- 类型的类型

 

      第一个问题,什么是泛型

      从字面上理解就是 类型的类型。泛型并不是 Java 独有的特性,事实上,Java的泛型在某种意义上是一种折中的实现,泛型的灵感来源于C++的模版类,但在实现的灵活性上又不同于C++的模版类。

      考虑下面这种情况,当我们调用一个方法的时候,需要传入一个对象作为参数,实际调用的时候,我们传入的是这个参数的子类,这种情况是很常见的。

 

            void method(Father father) {}

            method(new Son());

      Son 是 Father 类的子类。有时候 Son 可能已经继承了其他的父类,由于 Java 是单继承,Son 不能再继承Father 类,这种情况下可以考虑使用接口。

            class Son extends OtherFather implements Father ()
            method(new Son());

      使用接口消除了单继承的局限性,然而,有时候即使使用接口也是有局限性的,因为我们仍然要给 method() 方法指定具体的接口类型,就是 Father.

      有没有一种方法可以不指定具体的参数类型,而在需要时再指定?实势造泛型!

      入门 -- 一个简单的泛型使用

      容器类是泛型出现的原动力之一。See the following sample code:

		List<String> list = new ArrayList<String>(); 
		list.add("add string");
		list.add(new Integer(1)); // Can not compile

     上边的代码是编译不过的,可见,泛型在编译期就会检查传入的参数类型是否正确,也许这也是 JDK1.5推出泛型的原因之一吧。这种检查是合理的,如果把尖括号的内容去掉,就可以编译通过,但是,到运行时我们很可能会得到一个 CanNotCastException,容器大多时候只用来保存同一类型的对象。既然我们可以在编译期就能避免这样的错误,何苦而不为呢。

      List<String> 的意思是,一个保存String对象的List(不知道这种说法是否正确)。甚至可以简单地直接用一个字母<T> 代替 <String>, List<T>的意思是,一个保存类型为 T 的对象的List.

		List<T> list = new ArrayList<T>();

      泛型不仅可以用于容器类,还可以用于一般类,方法,接口,内部类等。以下的声明都是正确的。

		class Tuble<A, B, C> {}
		public interface Interface<T> {}
		abstract class Generator<T> {
			T temp;
			T gen() { return temp; }
		}

 

// P.484  The mystery of erease -- bounds, sel-bounding,wildcard, arrays of generic, issue of generic

      Erease -- 神秘的“消磁”

      Eease 的直译意思是擦去,除去,有人翻译为“消磁”。

            Class clz = List<Stirng>.class; // Can not compile

      上面的代码是编译不过的。因为 (new ArrayList<String>).getCLass() 与 (new ArrayList<Integer>).getCLass() 是相等的。在知道 List<T> 的类型仍是 List.class. 这就是 erease, 就是在运行时,类型T好象是被“抹去”了。

	class EreaseTest {
		void fun() { System.out.print("fun()"); }
	}
	
	class Erease<T> {
		private T obj;
		Erease(T type) {
			this.obj = type;
		}
		void invoke() { obj.fun(); } // Can not compile
	}

      上面的代码明显编译不过的,因为在知道 T 的具体类型之前就调用 obj.fun() 是不对的,编译器并不知道 T 中注册了fun() 方法,那么怎么让编译器知道 T 中注册了fun() 方法呢?

	class Erease<T extends EreaseTest> {
		private T obj;
		Erease(T type) {
			this.obj = type;
		}
		void invoke() { obj.fun(); } // Can not compile
	} 

      用 extends 关键字给泛型指定一个边界,编译器就足够聪明可以知道 T 中注册了哪些方法。extends 并不是继承的意思,Erease<T extends EreaseTest> 意思是指,类型参数 T 可以是 Erease类或者其任何子类。

     这一点上与 C++ 的模版类的实现不同,C++中是允许在知道 T 的具体类型前对 T 进行方法调用的。Java是在1.5之后才有泛型的,可能是也要考虑与1.4之前的版本兼容,泛型只用于编译期的静态类型检查,运行时,所有关于泛型的信息会被“除去”,也就是erease.

      在指定边界的时候可以使用通配符 ?, List<? extends Erease> 的意思是,一个任何 Erease 子类的List, 他与 “一个保存任何Erease子类的List” 是完全不一样的。

      可以把边界指定为自身,例如

	class SelfBound<T extends SelfBound> {}

      如果子类在父类参数类型中出现,还有一种叫法是CRG-- Curiously Recurring Generic.

	class Father<T> {}

	class Son extends Father<Son> {}

      这是相当有意义的,可以使得父类 Father 成为子类Son 的一个模版。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值