虽是读书笔记,但是如转载请注明出处http://segmentfault.com/blog/exploring/
..拒绝伸手复制党
概述
Java泛型核心概念: 告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
泛型的主要目的:
- 制定容器要持有什么类型对象,而且由编译器来保证类型的正确性。
- 简单而安全的创建复杂模型
泛型不能做什么:不能显示地引用运行时类型操作,例如转型,instanceof and new表达式。(因为擦除)
简单泛型(三个使用场景)
- 一个元组类库:创建元组,返回一组任意类型的对象。(它将一组对象直接打包存储于其中一个单一对象)
- 一个堆栈类:
- RandomList :构建一个可以应用于各种类型的对象的工具。
泛型接口
泛型方法的几个应用场景:
- 杠杆利用参数推断
-
可变参数(T ... args)与泛型方法(makelist实现util.Arrays.asList方法相同的功能)
用于Generator的泛型方法: 使用泛型方法创建Generator对象,大大减少了我们要编写的代码java
public BasicGenerator(Class<T> type){ this.type = type; } public T next(){ try{ return type .newInstance(); } catch(Exception e){ throw new RuntimeException(e); } } }
-
简化元组的使用 : 通过使用泛型方法整个各个Tuple类,重载static方法创建元组
java
public class Tuple { public static <A,B> TwoTuple<A,B> tuple(A a, B b) { return new TwoTuple<A,B>(a, b); } public static <A,B,C> ThreeTuple <A,B,C> tuple(A a, B b, C c) { return new ThreeTuple<A,B,C>(a, b, c);} public static <A,B,C,D> FourTuple<A,B,C,D> tuple(A a, B b, C c, D d) { return new FourTuple<A,B,C,D>(a, b, c, d); } }
一个Set实用工具
-
泛型用于内部类和匿名内部类
构建复杂模型
使用泛型可以简单而安全的创建复杂模型
擦除
Java泛型是通过擦除来实现的,namley 在使用泛型的时候,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。
在java泛型代码内部,无法获得任何有关泛型参数类型的信息。因此,你无法知道用来创建某个特定实例的实际的类型参数。比如List和List在运行时是相同的类型。
擦除的正当理由是从非泛华代码到泛华代码的转变过程,以及不破坏现有类库的情况下,将泛型融入Java语言。擦除使得现有的非泛型客户端代码能够在不改变的情况下继续使用,直至客户端准备好用泛型重写这些代码。
边界处的动作: 非泛型和泛型版本的相似的两个类通过javap -c 命令反编译可以发现字节码是相同的,就是说在运行时使用泛型的代码和普通代码没有什么区别。泛型中的所有动作都发生在边界处—对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。这有助于澄清对擦除的混淆,所谓边界,就是发生动作的地方。
擦除的补偿
泛型不能做:
-
instanceof
解决方法:使用类型标签,利用动态的isInstance判断java
public class ClassTypeCapture<T> { Class<T> kind; //类型标签 public ClassTypeCapture(Class<T> kind){ this.kind = kind; } }
-
泛型不能做:new表达式
解决方法:传递一个显示的工厂对象,限制其类型,使得只能接受实现这个工厂的类。java
public interface FactoryI<T> { T create(); } public class Foo2<T> { private T x ; //工厂 public <F extends FactoryI<T>> Foo2(F factory){ x = factory.create(); } public static void main(){ new Foo2<Integer>(new IntegerFactory()); new Foo2<Widget>(new Widget.Factory()); } }
泛型不能做:创建数组 T[] array
解决方法: 想要创建泛型数组的时候都使用ArrayList. orz...或者使用类型标记
如果实在想创建泛型数组,那么唯一方式就是创建一个被擦除类型的新数组(对象数组),然后对其转型。
java
public class GenericArray<T> { private T[] array ; public GenericArray(int sz){ //创建一个对象数组,然后对它转型 array = (T[])new Object[sz] ; } }
但是这样并不是很好,因为有了擦除,数组运行时候类型就只能是Object[],如果在创建时候对其转型为T[],那么编译器该数组的实际类型就会丢失,而编译器可能会错过潜在的错误检查。所以最为可靠的方式:在集合内部使用Object[],然后当你使用数组元素时,添加一个对T的转型。
java
private Object[] array; public T get(int index) { return (T) array[index];//Object转型为T } public T[] rep() { return (T[])array; //Object转型为T }
所以最终解决办法是使用类型标记:
java
T[] array ; public Constructor(Class<T> type, int sz) { array = (T[]) Array.newInstance(type, sz); }
边界
通配符
问题
自限定的类型
动态类型安全
异常 ....
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。