JAVA泛型其实是类型的类型,比如List数组我们可以接受任意类型的元素存储在数组当中,也可以在定义数组时,指定集合当中只存储某一类型。可以说泛型给我们的程序代码提供了可扩展性。
关于泛型的声明,如何使用,这篇暂不介绍,我们从比较容易困惑的地方开始
<? extend T> T可以是任意具体类型
举个例子如果我们定义了一个方法,接受List<Number> 类型的参数,那我们是否可以传递List<Integer>进去,答案是否定的
<? extend T> 表示的是当前泛型当中的元素,必须是T或者T类型的子类或者实现类
我们来看个例子,以下是三个类型的继承关系
class Fruit{}
class Apple extends Fruit{}
class Orange extends Fruit{}
public class Test {
public static void main(String[] args) {
List<Apple> apples = new ArrayList<>();
apples.add(new Apple());
List<Fruit> fruits = new ArrayList<>();
fruits.add(new Fruit());
Test.Transfer<Fruit> transfer = new Test.Transfer<Fruit>();
Fruit f1 = transfer.transfer(apples);
Fruit f2 = transfer.transfer(fruits);
}
static class Transfer<T>{
T transfer(List<? extends T> list) {
return list.get(0);
}
}
}
这里我们给出了一个静态内部类,用来转化,传入的是List对象,获取到List当中的元素,因为List当中存放的元素都是T类型的子类,所以我们在返回值上设定为T,是没有问题的
但是有个新的问题,如果我们想往List<? extends Fruit>当中添加元素时,却不能添加成功
List<? extends Fruit> fruits = new ArrayList<Apple>();
fruits.add(new Fruit()) //编译不通过
原因在于,当我们往集合当中添加元素时,因为集合的声明是Fruit的子类,所以我们并不清楚是具体的那个子类,Apple或者是Fruit,这个时候编译器就会提示报错了,接下来就是关于泛型的另外一个使用方式了
<? supper T>表示集合当中存放的必须是T类型或者T以上的父类型
public class Test {
public static void main(String[] args) {
List<Fruit> fruits = new ArrayList<>();
List<Apple> apples = new ArrayList<>();
Test.Transfer transfer = new Test.Transfer<Apple>();
transfer.transfer(fruits,new Apple());
transfer.transfer(apples, new Apple());
}
static class Transfer<T>{
void transfer(List<? super T> list,T item) {
list.add(item);
}
}
}
基于以上方式实现,就可以在数组当中添加不同的类型
总结 extends 和 supper 一个是用来查询集合当中的元素,另一个是用来往集合当中添加元素
类型擦除
泛型当中如果我们定义的是Node<T> 那么编制出来的T就是Object, 如果是 ? extends List 那么编译出来就是List
1、JAVA当中不允许创建泛型类型的数组
Object[] list = new String[2];
list[0] = "abc";
list[1] = 123;
上面这段代码我们在编译时是没有问题的,但是当执行的时候,就会抛出类型转换异常,原因是String类型的数组不能存放整形
如果我们定义的集合是List[] list = new ArrayList<String>[];
但是在存放数据时,保存的是list[0] = new ArrayList<Integer>; list[1] = new ArrayList<String>; 因为java当中存在类型擦除,所以ArrayList<String> 和ArrayList<Integer> 是一样的!为了避免这种问题发生,所以就不允许创建泛型类型的数组。
2、对于泛型代码,JAVA编译器会生成一个bridge method
public class Node<T> {
public T data;
public Node(T data) {
this.data = data;
}
public void setData(T data) {
System.out.println("set Node data");
this.data = data;
}
public static void main(String[] args) {
MyNode myNode = new MyNode(5);
Node node = myNode;
node.setData("adb"); //抛出异常
}
}
class MyNode extends Node<Integer>{
public MyNode(Integer data) {
super(data);
}
public void setData(Integer data) {
System.out.println("set myNode data");
super.data = data;
}
}
上面这段代码在编译时是可以的,但是运行时就会抛出异常错误,原因在于在MyNode方法当中编译时会生成一个BridgeMethod
class MyNode extends Node {
// Bridge method generated by the compiler
public void setData(Object data) {
setData((Integer) data);
}
public void setData(Integer data) {
System.out.println("MyNode.setData");
super.setData(data);
}
// ...
}
这就是为什么不能转化String为Integer的原因
3、关于泛型使用instanceOf
我们无法对泛型代码直接使用instanceof ArrayList<Integer> 因为对编译器来说ArrayList<Integer>和ArrayList<String>是同一个类型,如果我们想判断是否为ArrayList的话,可以通过这种方式来调用
public static void rtti(List<?> list) {
if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type
// ...
}
}
以上就是关于泛型的一些内容,如有问题欢迎指正