Java泛型
什么是泛型
- 泛型本质是指类型参数化。意思是允许在定义类、接口、方法时使用类型形参,当使用时指定具体类型,所有使用该泛型参数的地方都被统一化,保证类型一致。如果未指定具体类型,默认是Object类型。集合体系中的所有类都增加了泛型,泛型也主要用在集合。
- 泛型一般在集合中使用居多
如果定义泛型
- 泛型类 在这里
T表示未知类型,泛型类型可以是多个,一般用K V E M表示
- 泛型类 是在实例化的时候声明泛型的类型
public class People<T>{
}
- 泛型接口 在这里
T表示未知类型 和泛型类的定义一样
public interface PeopleImp<T>{
}
- 泛型方法 在这里
T表示未知类型
- 泛型方法 是在使用时声明使用的类型,注意这里和泛型类定义的泛型可能不是一致的
- 泛型方法 声明
<T>才是泛型方法
public <T> void isPeople(T people){
}
泛型的好处
- 在编译时确定类型,可以避免
ClassCastException
- 保证类型的安全,个人理解为一种强制的约束
泛型的限制和规则
如果我们定义了泛型,而不去指定它的具体类型,那么默认是Object
泛型的类型参数只能是引用类型,不能使用值类型。
public class People<T>{
}
public static void main(String[] args) {
People<int> people = new People<int>();
People<Integer> people = new People<Integer>();
}
泛型的类型参数可以有多个。
public static class People<T,E>{
}
泛型类不是真正存在的类,不能使用instanceof运算符。
public static void main(String[] args) {
People<Integer> people = new People<>();
if (people instanceof People<Integer> )
}
泛型类的类型参数不能用在静态申明。
public static T name;
static{
T name;
}
public static T getT(T t) {
return t;
}
- 以上代码报出异常:不能使一个静态引用指向一个非静态的类型T。
静态和非静态之分就在于静态是编译时类型,动态是运行时类型。
T代表未知类型,如果可以用于静态申明,因为是未知类型,
系统没法指定初始值,手动赋值也不行,因为不知道啥类型,
只有运行时才可以指定。而泛型存在的意义就是为了动态指定具体类型,
增强灵活性和通用性,所以用于静态声明违背了使用原则。为什么实例变量和实例方法可以使用呢?
因为当你使用实例变量或者方法时,就说明对象存在了,即代表泛型参数也指定了。未指定具体类型默认是Object类型。
public class Generic<T> {
private T key;
public T getT() {
return key;
}
public static T getT(T t) {
return t;
}
public static <T> T getT(T t) {
return t;
}
}
- 首先,要明确一点,泛型作用是确定具体类型。先看一个泛型方法,使用了泛型参数T作为返回值,当使用对象时来调用该方法时,T类型会变成具体类型。
- 第二个泛型方法是静态的,使用了T作为返回值和方法参数类型,但是静态方法是属于类的,类直接调用的话,T类型无法指定具体类型,那么该方法就没有意义。所以直接报错。(只是把泛型当做参数,并非真正的泛型方法)
- 第三个也是静态方法,但是该静态方法是自定义一个泛型参数,并非使用类型参数。所以当传入一个具体类型时,该静态方法的就是具体类型了。
- 两者静态方法的区别就是一个是使用泛型参数,一个是定义泛型方法。
如果定义了泛型,不指定具体类型,泛型默认指定为Ojbect类型。
泛型使用?作为类型通配符,表示未知类型,可以匹配任何类型。因为是未知,所以无法添加元素。
public static void main(String[] args) {
List<?> list = new ArrayList<>();
list.add(new People<String>());
}
类型通配符上限:<? extends T> 子类的泛型类型也属于泛型类型的子类,Java不允许将一个子类的泛型类型对象赋值给父类的对象声明,使用协变可以这么做,但是不推荐
?代表是T类型本身或者是T的子类型,也表示implements。常用于泛型方法,避免类型转换。也叫做协变,在这里不能直接添加list如果是父类的list,不能直接添加子类的对象
public void addPeople(List<? extends T> people){
}
public interface Animals {
void look();
}
public class Dog implements Animals {
@Override
public void look() {}
}
public class Pig implements Animals {
@Override
public void look() {}
}
public class AnimaDemo {
public static void main(String[] args) {
Pig pig = new Pig();
Dog dog = new Dog();
List<Pig> pigs = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
pigs.add(pig);
dogs.add(dog);
List<? extends Animals> animals = new ArrayList<>();
animals.add(dog);
animals = pigs;
}
public static void println(List<? extends Animals> anima){
System.out.println(anima.look())
}
println(pigs)
public static void println(List<Animals> anima){
System.out.println(anima.look())
}
println(pigs)
}
类型通配符下限。<? super T>,?代表T类型本身或者是T的父类型。也叫作逆变或者反变
public class AnimaDemo {
public static void main(String[] args) {
Pig pig = new Pig();
Dog dog = new Dog();
List<Pig> pigs = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
pigs.add(pig);
dogs.add(dog);
List<? super Animals> animals = new ArrayList<>();
List<Object> animalObjs = new ArrayList<>();
animals = animalObjs
animals.add(dog);
animals.add(new Object())
}
}
除了通配符可以实现限制,类、接口和方法中定义的泛型参数也能限制上限和下限。
泛型的类型擦除
- 无限制的类型擦除,如果泛型参数没有任何限制,比如
<T> <?> 那么擦除之后是Object
- 有限制的类型擦除,如果泛型参数有上限
<? extends Animals> ,擦除之后是Animals类型
- 有限制的类型擦除,如果泛型参数有上限
<? super Animals> ,擦除之后是Object类型
一个被人们讲烂了的例子
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
Kotlin的泛型
在Kotlin中泛型与Java一样都是不可变,但是和Java型变这块有一定的区别
在Kotlin中协变使用out表示逆变(下限)用in表示(上限)
协变
逆变
声明:本笔记是自己巩固泛型的时候对应网上文章和书籍的一些理解,如有搬运出于对原作者的尊重,会在尾部注明搬运链接,如果侵权请联删除
以上文章参考