什么是泛型
泛型就是编写模板代码来适应类型,好处就是不必强制转型,通过编译器对类型进行检查
List list = new ArrayList<>();
这就是泛型,集合限定了元素必须为String
类型,否则报错
泛型接口
除了在集合中使用泛型,有许多接口也用到了泛型,比如 Comparable
可以直接对String
类型的数组进行排序,因为String实现了Comparable
接口
public final class String implements Serializable, Comparable, CharSequence
String[] s = {"B", "A", "C"};
Arrays.sort(s);
System.out.println(Arrays.toString(s));
如何实习自定义类的排序呢?实现Comparable
接口
public class Person implements Comparable{
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 {
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;
}
}
注意静态方法不能使用
,应该与实例类型的泛型区分开:
public static Pair create(K first, K last) {
return new Pair(first, last);
}
擦拭法
虚拟机对泛型是一无所知的,视所有泛型为Object
,在需要转型的时候编译器会根据T类型自动为我们安全的强制转型
这就造成了使用泛型时一些的局限
- 不能是基本类型,因为
Object
无法持有基本类型 - 在获取
class
时因为擦拭法导致取到的对象是同一个对象 - 也无法判断泛型的类型
- 不能实例化泛型
可以借助Class
来实例化泛型对象
public Pair(Class clazz) {
first = clazz.newInstance();
last = clazz.newInstance();
}
泛型继承
public class IntPair extends Pair {
public IntPair(Integer first, Integer last) {
super(first, last);
}
}
子类获取父类的泛型类型:
public static void main(String[] args) {
Class 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
不是Pair
的子类
比如我们定义了一个方法,限定了传入的参数为Pair
类型,如果传入Pair
就会报错
static int add(Pair p) {
return p.getFirst().intValue() + p.getLast().intValue();
}
public static void main(String[] args) {
Pair 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 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[] pairs = (Pair[]) new Pair[2];