什么是泛型
泛型的本质就是类型检查和自动转型,类型约束,解决参数化类型;泛型分为泛型接口,泛型类,泛型方法。
作用:避免强制类型转换错误,提高代码复用性。
泛型简单创建与使用
//泛型接口
public interface Behavior<T> {
// 此处<E>表示声明泛型方法
// |
public <E> E eating(E food);
public String sleep();
}
//继承泛型接口
// 左边<T>是声明, 右边<T>是实例化 如果只写右边<T>,不写左边<T>,会报错的
// | |
public class Cow<T> implements Behavior<T> {
@Override
public <E> E eating(E food) {
return food;
}
@Override
public String sleep() {
return "开始睡觉!";
}
}
//如果上面Cow类不写<T> 继承Behavior接口后重写它的方法T会被替换成Object(以下示例),
//那么就失去泛型的意义
public class Cow implements Behavior {
@Override
public Object eating(Object food) {
return null;
}
@Override
public Object sleep() {
return null;
}
}
//泛型使用
public static void main(String[] args){
Cow<String> cow1 = new Cow<>();
String sleep = cow1.sleep();
String eating = cow1.eating("苹果");
System.out.println("牛吃完" + eating + sleep);
}
二、泛型的上下界
为什么泛型中会有上下界?我们先来看一段代码:
List<Fruit> fruits = new ArrayList<Apple>();//这个报错
ArrayList<Apple> apples=new ArrayList<>();
List<Fruit> fruits = apples;//报错
上面错误可以使用<? extends Fruit>解决
List<? extends Fruit> fruits = new ArrayList<Apple>();//这样就不报错啦
fruits.add(new Apple());//但是这又报错
使用了<? extends Fruit> 又不能写入了,是因为接收范围扩大了,程序不清楚需要传入什么类型。<? extends Fruit>表示该类型参数可以是Fruit或者Fruit的子类类型,编译时擦除为Fruit类型。<? extends Fruit> 表示只能获取信息,不能修改和增加信息。(只能消费)
那用它有什么用呢?
其实我们一般不这么写,一般用于场景化封装成一个函数供别人调用;如下:
public static float totalPrice(List<? extends Fruit> list) {
float totalPrice = 0f;
for (int i = 0; i < list.size(); i++) {
Fruit fruit = list.get(i);
float sum = fruit.price() * fruit.weight();
totalPrice += sum;
}
return totalPrice;
}
//使用
List<Apple> apples = new ArrayList<>();
apples.add(new Apple());
List<Banana> bananas = new ArrayList<>();
bananas.add(new Banana());
List<Watermelon> watermelons = new ArrayList<>();
watermelons.add(new Watermelon());
System.out.println(totalPrice(apples));
System.out.println(totalPrice(bananas));
System.out.println(totalPrice(watermelons));
修改和增加可以使用<? super Fruit>,<? super Fruit> 是只能生产不能消费。
//这样写了后只要是Banana的父类都可以传入进来
public void addBanana(List<? super Banana> list){
list.add(this);
}
Banana banana=new Banana();
List<Fruit> fruits = new ArrayList<>();
banana.addBanana(fruits);
泛型擦除
所有的T在运行期都会替换成Object类型。
public class Food<T> {
T name;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
}
//上面代码替换成以下代码
public class Food {
Object name;
public Object getName() {
return name;
}
public void setName(Object name) {
this.name = name;
}
}
泛型的静态问题
public class Beef<T> {
public static T t;//报错
public static T getTest(T t){//报错
return null;
}
public T t;//不报错了
public T getTest(T t){//不报错了
return null;
}
}
为什么上面代码加入static关键字报错呢?因为泛型类中的泛型参数的实例化是在定义泛型类型对象的时候指定的,而静态变量和静态方法不需要使用对象来调用。没创建对象,不能确定这个泛型参数是什么类型,所以错误。
但这样写不报错,因为这是泛型方法,泛型方法自己声明了,而不是使用泛型类的T。
public class Beef<T> {
public static <E> E test(E e){
return e;
}
}
Kotlin中的泛型
Kotlin中的泛型和Java泛型只是写法上不一样,其本质都是一样。
//泛型接口
interface KotlinFruit<in T> {
fun getInfo(): T //报错 int 对应Java中的<? super Fruit> 生成,不能消费
fun setInfo(t: T)//报错 out 对应Java中的<? extends Fruit> 消费,不能生产
//泛型方法
fun <E> getWeight(e: E): E
}
总结
- Java 中的<?> 对应 Kotlin <*> 无限制通配符,但在使用时就要各种类型转换。
- Java中的<? extends T> 对应 Kotlin 声明了类型的上界,只消费,不生产 。
- Java中的<? super T> 对应 Kotlin 声明了类型的下界,只生产,不消费 。
- 泛型提供编译期检查类型,自动转型,泛型约束,多重限制(使用&符号);
- 泛型在实例化的时候可以推断出实例化类型,泛型可以帮我们少写代码。
参考 https://pdai.tech/md/java/basic/java-basic-x-generic.html