总结自《java编程思想》
简单泛型
class TwoTuple<A, B>
{
public final A first;
public final B second;
public TwoTuple(A a, B b)
{
first = a;
second = b;
}
public String toString()
{
return "(" + first + "," + second + ")";
}
}
LinkedList<T> stack = new LinkedList<T>();
泛型接口
--Iterable Comparator---利用继承创建适配器
interface Generator<T>
{
T next();
}
class F implements Generator<Integer>
{
public Integer next() { return 1;}
}
泛型方法
--只要在返回值前面加上<T>就好了,就好像这个方法被无限次的重载过一样
----自动打包机制
class E
{
public <T> void g(T t)
{
System.out.println(t.getClass());
}
}
擦除
ArrayList<String>.getClass(); 和 ArrayList<Integer>.getClass(); 返回的值是相同的,这是因为在泛型代码内部,无法获得任何有关泛型参数的类型的信息。所以这里程序认为这两个数据结构的类是相同的。
Java泛型是用擦除来实现的。在使用泛型时,任何具体的类型信息都被擦除了,成为它们的原生类型。
原生类型:如果泛型类的类型参数部分没有制定上限,就会变成Object,如果有用到类似<? extends XX>这样的,就会变成类型上限。
擦除的问题
class G<T>{}
class D1<T> extends G<T>{}
class D2 extends G{} //with out any type parameter
//class D3 extends G<?>{} //error
编译器无法知道放置到泛型容器中的对象的T的具体信息,但仍旧可以在编译期确保放置到容器中的对象具有相应的T类型
泛型中的所有动作都发生在边界处。对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。
泛型数组
这样的话是没问题的。
class ListOfGenerics<T>{
private List<T> array = new ArrayList<T>();
public void add(T item) {array.add(item);}
public T get(int index) {return array.get(index);}
}
这样的话,程序能编译但不能运行,永远都不能创建这个确切类型的数组。即使gia已经被转型为Generic[Integer],在运行时,它仍然是Object数组。
class Generic<T>{}
class ArrayOfGeneric{
static Generic<Integer>[] gia;
}
成功创建一个泛型数组的唯一方法:创建一个被擦除类型的新数组,然后对其转型。
最好的用法是,在集合内部使用Object类型的数组,在使用它的时候在转型为T[]。比如在return时候,转型。
class GenericArray<T>{
private T[] array;
public GenericArray(int sz)
{
array = (T[])new Object[sz];
}
}
通配符
如果想在两个类型之间简历某种类型的向上转型关系,就可以用到通配符,也就是问号。
这样的话,编译不会有问题,但运行时候会出错。因为即使Apple是Fruit的子类型,Apple的List不等价与Fruit的List。
class Fruit {}
class Apple extends Fruit {}
public class NonCovariantGenerics{
List<Fruit> flist = new List<Apple>();
}
通配符的出现是为了制定泛型中的类型范围
<?> 无限定通配符
有时需要允许多个参数中的一个或几个是不限定类型的
public class UnboundedWildcards{
static void writeTo(List<? super Apple> apples){
static Map map1;
static Map<?, ?> map2; //其实一般的就等价于map1
static Map<String, ?> map3;
}
}
<? extends T> 有上限的通配符 任何从T继承的类型 比起上面,减小了范围
在这里,不能向里面添加对象 。你甚至不能像你刚声明过的Apple类型的数组里放一个Apple对象了!!
lass Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
public class Wildcards{
static void main(String[] args){
List<? extends Fruit> flist = new ArrayList<Apple>();
//flist.add(new Apple()); //ERROE
//flist.add(new Fruit()); //ERROR
//flist.add(new Object()); //ERROR
//你甚至不能像你刚声明过的Apple类型的数组里放一个Apple对象了!!
}
}
<? super T> 有下限的通配符 ????有一定的写操作能力????
class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
public class SuperTypeWildcards{
static void writeTo(List<? super Apple> apples){
apples.add(new Apple());
apples.add(new Jonathan());
//apples.add(new Fruit()); //这样的话会报错
}
}
参数Apple是Apple的某种基类型的List,这样就知道想其中添加Apple或Apple的子类型是安全的。但是,添加Fruit的话,就是不安全的,违反静态类型安全。
一般而言,通配符能干的事情,都能被泛型方法取代
自限定
自限定将采取额外的步骤,强制泛型当作自己的边界参数来使用。
class SelfBounded<T extends SelfBounded<T>>{}
class A extends SelfBounded<A>{} //这样会要求 将 正在定义的类 当作参数 传递给 基类,它可以保证类型参数必须与正在被定义的类的类型相同
class B extends SelfBounded<A>{} //OK,B也是SelfBounded的到处类,
class D{}
//class E extends SelfBounded<D>{} //不能使用不是SelfBounded的类型参数
//can't do this
class F extends SelfBounded<>{}