1.泛型的基本概念:
泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type)的应用,也就是说所操作的数据类型被指定为一个参数,在用到的时候在指定具体的类型。
这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。
2.泛型中标记符的含义:
E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 通配符 表示不确定的java类型
3.泛型的定义和使用
泛型方法:
public static <T> void display(T t) { //首先,泛型的声明,必须在方法的修饰符(public,static,final,abstract等)之后,返回值声明之前。然后,和泛型类一样,可以声明多个泛型,用逗号隔开。
System.out.println(t.getClass());
}
泛型接口:
public interface Generator<T> {
public T next();
}
定义一个泛型类十分简单,只需要在类名后面加上<>,再在里面加上类型参数:
如下:
class AA <T> {
private T value;
public AA (T value){
this.value = value;
}
}
泛型的定义可以有多个参数类型,其定义类似于:
public class AA <T,U>{......} //注 :类型变量使用大写形式,可以表示任意类型
在定义泛型的类中,通过get方法获取值时需要对值得类型进行限定,类似于:
public static <T> T get(T t1,T t2) {
return t1;
}
其中T的类型默认为Object,如果需要对T的类型设置限定,以便进行一些逻辑处理,比如:
public static <T> T get(T t1,T t2) {
if(t1.compareTo(t2)>=0);// 由于T默认为Object类型,则会出现编译错误 , 要想使用compareTo方法必须实现Comparable或Serializable接口
return t1;
}
可做如下处理:
public static <T extends Comparable> T get(T t1,T t2) { //添加类型限定
if(t1.compareTo(t2)>=0);
return t1;
}
注意:1、不管该限定是类还是接口,统一都使用关键字 extends
2、可以使用&符号给出多个限定,如下:
public static <T extends Comparable&Serializable> T get(T t1,T t2)
3.如果限定既有接口也有类,那么类必须只有一个,并且放在首位置
public static <T extends Object&Comparable&Serializable> T get(T t1,T t2)
4.泛型与子类
Child是Parent的子类,List<Child>却不是List<Parent>的子类。
因此:List<Object> list = new ArrayList<String>()是错误的。
原因:
如果上面是正确的,那么:
List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2
lo.add(new Object()); // 3
String s = ls.get(0); // 4,将object转换为string将失败。
如果要实现继承关系的泛型集合,则需要使用通配符 ? extends 、 ? super
“? extends”是泛型类型的子类型相关性成为现实
1.向上造型一个泛型对象的引用
List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
2.向下造型一个泛型对象的引用
List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits;
泛型子类解释:
如果类型A是类型B的子类,那么C<A> 是 C<? extends B> 的子类型
如果类型B是类型A的超类型(父类型),那么C<B> 是 C<? super A> 的子类型
原因:为什么使用通配符标记能行得通?
假设:
Apple[] apples = new Apple[ 1 ];
Fruit[] fruits = apples;
fruits[ 0 ] = new Strawberry(); //想要在 fruits 中添加元素
上述代码可以编译,但在运行时抛出异常,fruits中添加的元素可能不是apples类型
现在我们可以使用通配符把相关的代码转换成泛型,
List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
fruits.add( new Strawberry());
会发现 这次,代码就编译不过去了?????
因为 Java编译器会阻止你往一个Fruit list里加入strawberry,在编译时我们就能检测到错误,在运行时就不需要进行检查来确保往列表里加入不兼容的类型了,事实上你不能够
往一个使用了? extends的数据结构里写入任何的值。这个? extends T 通配符告诉编译器我们在处理一个类型T的子类型,但我们不知道这个子类型究竟是什么。因为没法确定,为
了保证类型安全,我们就不允许往里面加入任何这种类 型的数据。另一方面,因为我们知道,不论它是什么类型,它总是类型T的子类型,当我们在读取数据时,能确保得到的数据是
一个T类型的实例
List<Fruit> fruits = new ArrayList<Fruit>();
List<? super Apple> = fruits;
我们看到fruits指向的是一个装有Apple的某种超类(supertype)的List。同样的,我们不知道究竟是什么超类,但我们知道 Apple和任何Apple的子类都跟它的类型兼容。既然这个
未知的类型即是Apple,也是GreenApple的超类,我们就可以写入:
fruits.add( new Apple());
fruits.add( new GreenApple());
static class aaa{}
static class bbb extends aaa{}
public static void Method(){
List<aaa> ll = new ArrayList<aaa>();
List<?super bbb> lll = ll;
lll.add(new bbb()); //编译通过 编译机能确保新添加的元素是bbb超类的子类,因为你只能添加bbb对象或者其子类,当添加父类时编译机会出现警告 //从集合lll中获取的元素类型为Object
List<bbb> ll2 = new ArrayList<bbb>();
List<?extends aaa> lll2 = ll2;
lll2.add(new bbb());//编译报错 因为编译机不能确保新添加的元素和集合里之前的元素类型是否一致,即不能确定你添加的是哪个子类,所以不允许往集合里添加任何数据
}
总结 ? extends 和 the ? super 通配符的特征,我们可以得出以下结论:
◆ 如果你想从一个数据类型里获取数据,使用 ? extends 通配符
◆ 如果你想把对象写入一个数据结构里,使用 ? super 通配符
◆ 如果你既想存,又想取,那就别用通配符。
另外:
泛型数组:泛型数组 只能作为参数类型 或者 函数参数 不能作为实际块内代码存在
即,可以这样定义:ArrayList<T>[] list = new ArrayList<T>[n]; //作为参数类型
public static <T> T[] fun1(T...arg){ // 接收可变参数 //作为函数参数
return arg ; // 返回泛型数组
}
不能这样定义:
public class GArrayT<T> {
private T[] arr;//这样是不行的
public GArrayT(int capacity) {
arr = new T[capacity];
System.out.println("End");
}
}