Java泛型整理

总结自《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<>{}



猜你喜欢

转载自blog.csdn.net/sinat_15901371/article/details/80905846