泛型的概念
所谓泛型就是编写一种模板实现任意数据类型都可使用的目的,既实现了编写一次,万能匹配,又通过编译器保证了类型安全
向上转型
如下所示,ArrayList<T>
实现了List<T>
,所以ArrayList
可以向上转型为List
public class ArrayList<T> implements List<T> {
...
}
// 由于ArrayList实现了List,所以如下代码不会报错
List<String> list = new ArrayList<String>();
注意泛型的继承关系:可以把ArrayList<Integer>
向上转型为List<Integer>
(T
不能变!),但不能把ArrayList<Integer>
向上转型为ArrayList<Number>
(T
不能变成父类),因为ArrayList<Integer>
和ArrayList<Number>
是同一个对象
泛型使用
- 使用泛型时,把泛型参数
<T>
替换为需要的class类型,例如:ArrayList<String>
,ArrayList<Number>
等; - 可以省略编译器能自动推断出的类型,例如:
List<String> list = new ArrayList<>();
; - 不指定泛型参数类型时,编译器会给出警告,且只能将
<T>
视为Object
类型;
// sort
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Person[] ps = new Person[] {
new Person("Bob", 61),
new Person("Alice", 88),
new Person("Lily", 75),
};
Arrays.sort(ps);
System.out.println(Arrays.toString(ps));
}
}
// Person实现Comparable<T>接口
class Person implements Comparable<Person> {
String name;
int score;
Person(String name, int score) {
this.name = name;
this.score = score;
}
public int compareTo(Person other) {
return this.name.compareTo(other.name);
}
public String toString() {
return this.name + "," + this.score;
}
}
泛型的编写
过程
1、按照某种类型,例如:String
,来编写类
public class Pair {
private String first;
private String last;
public Pair(String first, String last) {
this.first = first;
this.last = last;
}
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
}
2、标记所有的特定类型,这里是String
public class Pair {
private String first;
private String last;
public Pair(String first, String last) {
this.first = first;
this.last = last;
}
public String getFirst() {
return first;
}
public String getLast() {
return last;
}
}
3、把特定类型String
替换为T
,并申明<T>
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;
}
}
泛型不可用于静态方法
如下:
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() { ... }
public T getLast() { ... }
// 对静态方法使用<T>:会导致编译错误,我们无法在静态方法create()的方法参数和返回类型上使用泛型类型T
public static Pair<T> create(T first, T last) {
return new Pair<T>(first, last);
}
}
// 静态方法泛型是单独针对静态方法的泛型和class类的泛型没关系,T!==K
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() { ... }
public T getLast() { ... }
// 静态泛型方法应该使用其他类型区分:
public static <K> Pair<K> create(K first, K last) {
return new Pair<K>(first, last);
}
}
多个泛型类型
public class Pair<T, K> {
private T first;
private K last;
public Pair(T first, K last) {
this.first = first;
this.last = last;
}
public T getFirst() { ... }
public K getLast() { ... }
}
// 使用
Pair<String, Integer> p = new Pair<>("test", 123);
// Java标准库的Map<K, V>就是多泛型的例子
Map<String,Integer> map = new Map<>("age", 26)
擦拭法
public class MyUtils<T> {
private T type_variable;
MyUtils() throws Exception{
Type t = getClass().getDeclaredField("type_variable").getGenericType();
Log.i("xbh", t.toString());
}
}
// 调用
MyUtils<String> m = new MyUtils<String>();
理论上是输出String,可是就是由于擦拭法的原因,输出是T。
所以擦拭法是泛型在运行的时候,不会暴露出真是类型,可以说是被拭去了。
原因:java语法中认可泛型的,但是虚拟机不认可,所以如果你想获取真实类型的话,必须在编译的时候就要指定了。
extends通配符
使用类似<? extends Number>
通配符作为方法参数时表示:
- 方法内部可以调用获取
Number
引用的方法,例如:Number n = obj.getFirst();
; - 方法内部无法调用传入
Number
引用的方法(null
除外),例如:obj.setFirst(Number n);
。
即一句话总结:使用extends
通配符表示可以读,不能写。
使用类似<T extends Number>
定义泛型类时表示:
- 泛型类型限定为
Number
以及Number
的子类。
super通配符
使用类似<? super Integer>
通配符作为方法参数时表示:
- 方法内部可以调用传入
Integer
引用的方法,例如:obj.setFirst(Integer n);
; - 方法内部无法调用获取
Integer
引用的方法(Object
除外),例如:Integer n = obj.getFirst();
。
即使用super
通配符表示只能写不能读。
使用extends
和super
通配符要遵循PECS原则。
无限定通配符<?>
很少使用,可以用<T>
替换,同时它是所有<T>
类型的超类。
泛型和反射
部分反射API是泛型,例如:Class<T>
,Constructor<T>
;
可以声明带泛型的数组,但不能直接创建带泛型的数组,必须强制转型;
可以通过Array.newInstance(Class<T>, int)
创建T[]
数组,需要强制转型;
同时使用泛型和可变参数时需要特别小心。
标签:last,String,Pair,泛型,genericity,public,first From: https://www.cnblogs.com/yangguanglei/p/16847414.html