文章目录
- 泛型
- 泛型类
- 泛型接口
- 泛型方法
- 泛型的通配
- 泛型的擦除
泛型
参数化类型。
泛型的好处:
- 省去了类型强转的麻烦(比如:Object类、interface接口等都需要类型强转)
- 将运行期遇到的问题转移到了编译期
泛型类
所谓泛型类, 就是把泛型定义在类上
格式:
class 类名<泛型类型1,...> {
}
注意事项:
- 默认类型
- 定义了泛型,但是未写在
<>
中,会将其直接当做Object使用。
eg:
- 定义了泛型,但是未写在
// 如果我们有一个使用了泛型写法的地方, 当我们使用这段代码的时候需要传入泛型,
// 如果没有传, 这个泛型默认为Object类型Holder2 holder3 = new Holder2(new AutoCar());
Object o = holder3.get();
- 泛型的使用写法
eg:
// jdk1.5版本的时候写法: 前后尖括号都要指明类型
User<String> user1 = new User<String>();// jdk1.7版本的时候, 对jdk1.5写法的简化, 本质上等价的
User<String> user1 = new User<>();
- 泛型类可以定义多个泛型
- 可以定义多个泛型,但不建议超过两个。
- 定义多个泛型,使用时,要么全部指定类型,要么全部不指定类型。
eg:
class User<T, E>{T data;E info;
}User<int, String> user = new User<>(2024 , "hello");
- 定义了多个泛型,也可以不使用
eg:
class User3 <T, E, K> {T name;E age;
}
-
泛型标识符
- 使用单个大写字母。比如:E、T、K、V、R
- E:element;
- T:type;
- K:key;
- V:value;
- R:return
- 使用单个大写字母。比如:E、T、K、V、R
-
泛型必须使用引用类型,不能使用基本类型
eg:
User<int> user = new User<>(); // 报错: 泛型必须使用引用类型User<Integer> user = new User<>();
-
泛型类, 定义了泛型之后, 泛型的作用域
-
- 在自己的类中间,会起作用。但是在子类中,不起作用。
-
- 在自己的类上面(指
public class User<T> extends Person<E, E>
),就是在自己的类定义这一行,可以使用这个泛型。
- 在自己的类上面(指
-
-
泛型在父子继承关系上的表现
- 如果继承时,未指定父类泛型,则为默认类型。Object
class Son1 extends Father{}
- 如果继承时,指定了父类类型,则为指定类型,无论子类定义泛型与否。
class Son2 extends Father<String>{} --> 父类变量类型为String
class Son3<E> extends Father<Integer>{} --> 父类变量类型为Integer
- 如果继承时,传入了子类指定的泛型,则父类与子类变量类型一致
class Son4<E> extends Father<E>{}
- 如果继承时,未指定父类泛型,则为默认类型。Object
eg:
public class Father<T> {T ft;
}class Son1 extends Father {}class Son2 extends Father<Integer> {}class Son3<E> extends Father<String> {}class Son4<T> extends Father<T> {}class Son5<E> extends Father<E>{}public class FatherDemo {public static void main(String[] args) {// 定义类的时候,没有指定Father的类型,所有默认类型为ObjectSon1 son1 = new Son1();Object ft = son1.ft;// Son2 定义时,未指定泛型,指定了 Father泛型为Integer,所以ft为IntegerSon2 son2 = new Son2();Integer ft1 = son2.ft;// 如果继承时,指定了父类类型,则为指定类型,无论子类定义泛型与否Son3<Integer> son3 = new Son3();String ft4 = son3.ft;// Son4 定义时,指定泛型T,指定了 Father泛型为T,所以ft类型和子类一致Son4<Integer> son4 = new Son4<>();Integer ft2 = son4.ft;// Son5 指定E,表示与符号无关Son5<String> son5 = new Son5<>();String ft3 = son5.ft;}
}
泛型接口
所谓泛型接口, 就是把泛型定义在接口上
格式:
interface 接口名<泛型类型1...>
举例:
interface Player<T,E> {E play(T data);
}// 在什么时候能指定它的类型?// 1. 实现接口的时候没有指定类型,则泛型的类型为默认的Object
class YoungPlayer implements Player{@Overridepublic Object play(Object data) {return null;}
}// 2. 如果实现时,指定为什么类型,则是什么类型
class OldPlayer implements Player<String, Integer>{@Overridepublic Integer play(String data) {return null;}
}// 3. 如果子类也有泛型,则与子类一致
class BigPlayer<K,V> implements Player<K,V>{@Overridepublic V play(K data) {return null;}
}
eg:
// 转换器的接口,把一个类型转换成另外一个类型
interface Converter<T, R> {R converter(T t);
}public class String2Date implements Converter<String, Date> {@Overridepublic Date converter(String s) {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");Date parse = null;try {parse = simpleDateFormat.parse(s);} catch (ParseException e) {throw new RuntimeException(e);}return parse;}
}
泛型方法
所谓泛型方法, 把泛型定义在方法上
格式:
<泛型类型> 返回类型 方法名(泛型类型...)
eg:
public class Demo {public static void main(String[] args) {A a = new A();Integer t = a.getT(10);String s = a.getT("hello");}
}class A{<T> T getT(T data){return data;}
}
注意事项:
方法上没有定义泛型,只是使用了泛型,不叫泛型方法。
泛型的通配
(看到源码,能明白含义即可。)
泛型不允许协变, 又想产生类似协变的效果, 又不想引入协变带来的问题(类型不匹配问题)
协变与逆变
- 协变就是,允许接收该类及该类的子类。
- 数组是支持协变的。
- 逆变就是,允许接收该类及该类的父类。
格式:
泛型通配符<?>
- 任意类型,如果没有明确,那么就是Object以及任意的Java类了
public void print(User<?> user)
? extends E
- 向下限定,E及其子类
public void print(User<? extends Number> user)
? super E
- 向上限定,E及其父类
泛型的擦除
- Java中的泛型并不是真的泛型, Java的泛型只存在于编译之前。
- 当Java中的泛型编译之后, 会把泛型编译成Object以及类型强转。