概述
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
泛型可以理解成接收数据类型的一种数据类型!
Java泛型介绍
类结构是面向对象中最基本的元素,如果我们的类需要有很好的扩展性,那么我们可以将其设置成泛型的。
泛类型在编译时定义,运行时才能确定具体类,Java 基础-面向对象
class DataHolder<T>{
T item;
public void setData(T t) {
this.item=t;
}
public T getData() {
return this.item;
}
}
泛型语法
- 在泛型
<T,V,...>可以有任意 - 在泛型
<T,V,...>,中T,V,..等等只能是引用类型,不能是基本数据类型 - 在给泛型指定具体类型后,可以传入该类型或者该子类型
- 简写
ArrayList<Dog> integers = new ArrayList<>()等价ArrayList<Dog> integers = new ArrayList<Dog>() ArrayList integers = new ArrayList()等价ArrayList<Object> integers = new ArrayList<Object>()
interface Car<T>{}
interface Car<T,V>{}
class Car<T>{}
class Car<T,V>{}
泛型自定义类
- 注意
- 普通成员可以使用泛型(属性,方法)
- 使用泛型的数组不能初始化
- 静态方法中不能使用类型的泛型
- 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)
- 如果在创建对象时,没有指定类型,默认Object
public class Cat <T,R ,M> {
String name;
T t;
R r;
M m;
T[] ts;
// ***报错 :因为数组在 new 的时候不能确定 T 的类型
// T[] ts = new T[8];
public Cat(String name, T t, R r, M m) {
this.name = name;
this.t = t;
this.r = r;
this.m = m;
}
/* 因为静态和类相关,在类加载时,对象还没有创建,
所以如果静态方法和静态属性使用了泛型,JVM无法完成初始化
*/
/*
static R r2;
public static void m1(M m){
}*/
}
泛型自定义接口
- 注意
- 接口中,静态成员也不能使用泛型(这个和泛型类规定的一样)
- 泛型接口的类型,在
继承接口或者实现接口时确定 - 没有指定类型,默认
Object
public interface IUsb<U,R> {
int n = 10;
// 静态成员不能使用泛型
// U name = "";
R get(U u);
void hi(R r);
void run(R r1,R r2 ,U u1 ,U u2);
// jdk1.8中,可以在接口中使用默认方法,也可以使用泛类型
default R method(U u){
return null;
}
}
public interface IAi extends IUsb<String,Double> {
void say(String r);
}
/**
* 继承接口:
* 当我们去实现IAi接口时,因为IA在继承IUsb接口的时候指定了 U->String R->Double,在实现IUsb接口的方法时,
* 使用String替换U,是Double替换R
*/
public class Ai implements IAi {
@Override
public Double get(String s) {
return null;
}
@Override
public void hi(Double aDouble) {
}
@Override
public void run(Double r1, Double r2, String u1, String u2) {
}
@Override
public void say(String r) {
}
}
/**
* 实现接口:
* 直接指定泛型接口的类型给 U 指定 Integer,给 R 指定 Float,
* 所以当实现 IUsb 方法时,会使用 Integer 替换 U, Float 替换 R
*/
public class Usb implements IUsb<Integer,Float>{
@Override
public Float get(Integer integer) {
return null;
}
@Override
public void hi(Float aFloat) {
}
@Override
public void run(Float r1, Float r2, Integer u1, Integer u2) {
}
}
泛型自定义方法
- 注意
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 当泛型方法被调用时,类型会确定
public class Car <E,F> {
public void run(){}
// 泛型方法
public <T,R> void fly(T t, R r){}
// 方法使用泛型
public void hello(E e){}
public <T,R> void hi(T t, R r){}
// 泛型方法也也可以使用类泛型混用
public <K> void ok(E e, K k){}
}
// 当调用方法时,传入参数,编译器,就会确定类型
Car car = new Car();
car.fly("宝马",100);
car.fly(100,100.1);
泛型的继承
1.泛型不具备继承性 , ArrayList<Object> integers2 = new ArrayList<String>();
泛型的通配符
<?>无限通配符: 支持任意泛型类型<? extends A>上界通配符: 支持 A 类以及A类的子类,规定了泛型的上限<? super A>下界通配符: 支持 A 类以及A类的父类,不限于直接父类,规定了泛型的下限
PECS原则
最后简单介绍下Effective Java这本书里面介绍的PECS原则。
- 上界<? extends T>不能往里存,只能往外取,适合频繁往外面读取内容的场景。
- 下界<? super T>不影响往里存,但往外取只能放在Object对象里,适合经常往里面插入数据的场景。