什么是泛型
泛型就是编写模板代码来适应类型,益处就是不用强制转型,通过编译器对类型进行查看
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];