Java笔记06 - 泛型
泛型是一种"代码模板"
什么是泛型
T
可以是任何class
. 编写一次模板, 创建任意类型的ArrayList
- 泛型是定义一种模板, 例如:
ArrayList<T>
, 然后再代码中为用到的类创建ArrayList<类型>
- 编写一次, 万能匹配, 通过了编译器又保证了类型安全
向上转型
- 通过应用接口, 实现向上转型
ArrayList<T> implements List<T>
:ArrayList<T>
向上转型为List<T>
- 模板之间没有任何继承关系:
ArrayList<Integer>
和ArrayList<Number>两者完全没有继承关系
- 在向上继承时, T不可变!
使用泛型
- 使用时, 如果不定义泛型类型, 默认是
Object
. - 如果泛型定义为
Object
没有发挥泛型的优势. - 编译器自动推断, 可以省略
List<Number> list = new ArrayList<>;
泛型接口
- 可以在接口中定义泛型类型, 实现此类接口必须实现正确的泛型类型
编写泛型类
- 一般来说, 泛型类用在集合类中, 我们很少编写泛型类
- 就是这个类, 可以针对某个特定的类型, 成为一个专门的类
- 在类后面定义一个
<T>
, 然后在这个类里面就有了这么一种类型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;
}
// 对于静态方法使用<T>
public static<K> Pair<K> create(K first, K last) {
return new Pair<K>(first, last);
}
}
多个反省类型
- 泛型可以定义多种类型.
<T, K>
- 使用的使用, 分别指出两种类型即可
Pair<String, Integer>
擦拭法
- 泛型是一种模板技术, 不同语言实现方式不同, java是擦拭法
- 擦拭法: 虚拟机对泛型其实一无所知, 所有的工作都是编译器做的
- Java的泛型是由编译器在编译时实行的, 编译器内部永远吧所有类型
T
视为Object
处理. - 在需要转型的时候, 编译器会根据
T
的类型, 自动为我们实行安全地强制类型.
java发型的局限
<T>
不能是基本类型, 例如int
, 因为实际类型是Object
.- 无法取得带泛型的
Class
:<T>
是Object
, 无论T
的类型是什么,getClass()
返回同一个Class
实例 - 无法判断带泛型的
Class
: 不存在Pair<String>.class
, 只存在唯一的Pair.class
- 不能实例化
T
类型: 只能通过反射传入实现.
不恰当的覆写方法
- 定义的
equals(T t)
不会覆写成功, 因为摩擦成equals(Object t)
, 这就是一个覆写方法了
泛型继承
class IntPair extends Pair<Integer> { public IntPair(Integer first, Integer last) {
super(first, last);
}
}
public class Pair<T> {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
}
// 可以直接使用: Integer ip = new IntPair(1, 2)
// 获取继承泛型类型方法 Class<IntPair> clazz = IntPair.class;
Type t = clazz.getGenericSuperclass();
if (t instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) t;
Type[] types = pt.getActualTypeArguments();
Type firstType = types[0];
Class<?> typeClass = (Class<?>) firstType;
System.out.println(typeClass);
}
- 因为java中引入了泛型, 所以只用
Class
来标识泛型不够用. 所以: java类型的体系如下:┌────┐
│Type│
└────┘
▲
│
┌────────────┬────────┴─────────┬───────────────┐
│ │ │ │
┌─────┐┌─────────────────┐┌────────────────┐┌────────────┐
│Class││ParameterizedType││GenericArrayType││WildcardType│
└─────┘└─────────────────┘└────────────────┘└────────────┘
extends通配符
Pair<Integer>
类型, 符合参数Pair<? extends Number>
: 上界通配符, 类型T上界限定在Number
- 方法参数签名
setFirst(? extends Number)
无法传递任何Number
类型给setFirst(? extends Number)
- 唯一的例外可以传入
null
, // ok, 但是后面排除NullPointerException
extends通配符的作用
- 可以通过
get
获取一个指定类型的返回结果 - 无法通过
set
进行设置和修改类型 - 使用
extends
通配符表示可以读, 不能写.
使用extends限定T类型
- 在定义泛型类型
Pair<T>
的时候, 可以使用extends
通配符来限定T
的类型
super通配符
- 允许
set(? super Integer)
传入Integer
的引用 - 不允许调用
get()
方法获得Integer
的引用 - 使用
<? super Integer>
通配符作为方法参数, 便是方法内部代码对于参数只能写, 不能读
对比extends
和super
通配符
<? extends T>
允许调用方法T get()
获取T
的引用, 但不允许调用方法set(T)
传入T
的引用(传入null
除外)<? super T>
允许调用方法set(T)
传入T
的引用, 但不允许调用方法T get()
获取T
的引用(获取Object
除外)
class Collections { public static <T> void copy(List<? super T> dest, List<? extends T> src) {
// 编辑器可以避免: 意外读取dest, 意外操作src. 安全操作数组
for (int i = 0; i < src.size(); i++) {
T t = src.get(i);
dest.add(t);
}
}
}
PECS原则
- Producer Extends Consumer Super
- 如果需要返回
T
, 它是生产者(Producer), 需要使用extends
通配符 - 如果需要写入
T
, 它是消费者(Consumer), 需要使用super
通配符 - 需要返回
T
的src
是生产者, 声明为<? extends T>
- 需要写入
T
的dest
是消费者, 声明为<? super T>
无限定通配符
- 无限定通配符(Unbounded Wildcard Type), 即只定义一个
?
void sample(Pair<?> p) {}
<?>
具体作用:- 不允许调用
set<T>
方法传入引用T
(null
除外) - 不允许调用
T get<>
方法并获取T
引用(只能获取Object
引用) - 换句话说: 既不能读, 也不能写, 只能做一些
null
判断
- 不允许调用
- 只做一些
null
判断, 大多数情况下, 引入泛型参数<T>消除<?>通配符
Pair<?>是所有的Pair<T>
的超类
Pair<Integer> p = new Pair<>(123, 456);Pair<?> p2 = p; // 安全的向上转型
泛型和反射
Class<T>
就是泛型- 调用
Class
的getSuperclass()
方法返回的Class
类型是Class<? super T>
- 构造方法
Constructor<T>
也是泛型
Class<Integer> clazz = Integer.class;Constructor<Integer> cons = clazz.getConstructor(int.class);
Integer i = cons.newInstance(123);
- 可以声明但泛型的数组, 但不能用
new
操作符创建, 必须通过强制转型实现.
// Pair<String>[] ps = null; // ok// Pair<String>[] ps = new Pair<String>[2]; // Cannot create a generic array of Pair<String>
Pair<String>[] ps = (Pair<String> []) new Pair[2];
- 使用泛型数组, 是有风险的, 必须扔到初始数组的引用, 直接进行强制转换
class ABC<T> {// T[] createArray() {
// return new T[5];
// }
T[] createArray(Class<T> cls) { // 借助`Class<T>创建泛型数组`
return (T[]) Array.newInstance(cls, 5);
}
}
谨慎使用泛型可变参数
疑问
int
和Integer
两种类型的关系和区别?- 谨慎使用泛型可变参数中, 为什么第二个会报错的原因没看懂.
以上是 Java笔记06 - 泛型 的全部内容, 来源链接: utcz.com/z/394767.html