共计 3235 个字符,预计需要花费 9 分钟才能阅读完成。
什么是泛型
泛型就是编写模板代码来适应类型,益处就是不用强制转型,通过编译器对类型进行查看
List<String> list = new ArrayList<>();
这就是泛型,汇合限定了元素必须为 String
类型,否则报错
泛型接口
除了在汇合中应用泛型,有许多接口也用到了泛型,比方 Comparable<T>
能够间接对 String
类型的数组进行排序,因为 String 实现了 Comparable
接口
public final class String implements Serializable, Comparable<String>, CharSequence
String[] s = {"B", "A", "C"};
Arrays.sort(s);
System.out.println(Arrays.toString(s));
如何实习自定义类的排序呢?实现 Comparable
接口
public class Person implements Comparable<Person>{
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {return age;}
public String getName() {return name;}
@Override
public int compareTo(Person o) {return this.getAge()-o.getAge();}
}
测试:
Person[] people = {new Person("lucy", 21), new Person("mary", 19), new Person("Mali", 33)};
System.out.println("排序前");
for (Person person : people) {System.out.println(person.getName() + ":" + person.getAge());
}
Arrays.sort(people);
System.out.println("排序后");
for (Person person : people) {System.out.println(person.getName() + ":" + person.getAge());
}
通过控制台输入就能够发现 Person
数组曾经依照年龄进行了升序排序
编写泛型
public class Pair<T> {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() {return first;}
public T getLast() {return last;}
}
留神静态方法不能应用<T>
,应该与实例类型的泛型辨别开:
public static <K> Pair<K> create(K first, K last) {return new Pair<K>(first, last);
}
擦拭法
虚拟机对泛型是无所不知的,视所有泛型为Object
,在须要转型的时候编译器会依据 T 类型主动为咱们平安的强制转型
这就造成了应用泛型时一些的局限
- 不能是根本类型,因为
Object
无奈持有根本类型 - 在获取
class
时因为擦拭法导致取到的对象是同一个对象 - 也无奈判断泛型的类型
- 不能实例化泛型
能够借助 Class<T>
来实例化泛型对象
public Pair(Class<T> clazz) {first = clazz.newInstance();
last = clazz.newInstance();}
泛型继承
public class IntPair extends Pair<Integer> {public IntPair(Integer first, Integer last) {super(first, last);
}
}
子类获取父类的泛型类型:
public static void main(String[] args) {
Class<IntPair> clazz = IntPair.class;
Type type = clazz.getGenericSuperclass(); // 取得带有泛型的父类
if (type instanceof ParameterizedType){ // 判断是否为参数化类型,即泛型
ParameterizedType p = (ParameterizedType) type;
Type[] types = p.getActualTypeArguments(); // 父类可能有多个泛型
Type firstType = types[0]; // 取第一个泛型
System.out.println(firstType.getTypeName());
}
}
extends
在平时的继承关系中 Integer
是Number
的子类,然而在泛型中 Pair<Integer>
不是 Pair<Number>
的子类
比方咱们定义了一个办法,限定了传入的参数为 Pair<Number>
类型,如果传入 Pair<Integer>
就会报错
static int add(Pair<Number> p) {return p.getFirst().intValue() + p.getLast().intValue();}
public static void main(String[] args) {Pair<Integer> p = new Pair<>(3,5);
Pair.add(p);
}
那如何传入 Integer
呢?这就须要用到 extends
通配符来解决了,革新一下那个办法
static int add(Pair<? extends Number> p) {return p.getFirst().intValue() + p.getLast().intValue();}
这种通配符被称为上界通配符,把泛型类型 T 的上界线定在了Number
应用 extends 须知:
在 add()
中是不能获取 Integer
的援用的,上面代码是无奈通过编译的,要求你强制转型
Integer first = p.getFirst();
因为咱们尽管限定了泛型的上界为 Number
,然而传入的具体类型到底是Integer
还是其余 Number
的子类是不晓得的,编译器只能确定肯定是 Number
的子类
也无奈传递 Number
的子类型给对象的set()
p.setFirst(new Integer(p.getFirst().intValue() + 100));
总结一下:
- 容许调用
get()
获取Number
的援用 - 不容许调用
set(? extends Number)
传入任何 Number 的援用
super
正好和 Number
相同,传入的是 Integer
以及它的父类
Pair<? super Integer> p
应用 super 须知:
public static void main(String[] args) {Pair<Integer> p = new Pair<>(3,5);
Pair.add(p,5);
}
static void add(Pair<? super Integer> p,Integer n) {p.setFirst(n);
}
这段代码是能够被失常编译的,因为限定了下界为 Integer
,无论传入Integer
还是它的父类都是能够的
然而无奈应用 Integer 接管 get()
的返回值,因为无奈确定具体返回的是 Integer
还是它的父类,惟一能够接管的是Object
static void add(Pair<? super Integer> p,Integer n) {Object first = p.getFirst();
}
所以比照 extends
和super
能够发现:
<? extends Number>
可读不可写
<? super Integer>
可写不可读
补充
申明泛型数组时,不能用 new
操作符创立数组,需强制转型
Pair<Integer>[] pairs = (Pair<Integer>[]) new Pair[2];