文章目录
泛型
参数化类型。
泛型的好处:
- 省去了类型强转的麻烦(比如:Object类、interface接口等都需要类型强转)
- 将运行期 遇到的问题转移到了编译期
泛型类
所谓泛型类, 就是把泛型定义在类上
格式:
java
class 类名<泛型类型1,...> {
}
注意事项:
- 默认类型
- 定义了泛型,但是未写在
<>
中,会将其直接当做Object使用。
eg:
- 定义了泛型,但是未写在
java
// 如果我们有一个使用了泛型写法的地方, 当我们使用这段代码的时候需要传入泛型,
// 如果没有传, 这个泛型默认为Object类型
Holder2 holder3 = new Holder2(new AutoCar());
Object o = holder3.get();
- 泛型的使用写法
eg:
java
// jdk1.5版本的时候写法: 前后尖括号都要指明类型
User<String> user1 = new User<String>();
// jdk1.7版本的时候, 对jdk1.5写法的简化, 本质上等价的
User<String> user1 = new User<>();
- 泛型类可以定义多个泛型
- 可以定义多个泛型,但不建议超过两个。
- 定义多个泛型,使用时,要么全部指定类型,要么全部不指定类型 。
eg:
java
class User<T, E>{
T data;
E info;
}
User<int, String> user = new User<>(2024 , "hello");
- 定义了多个泛型,也可以不使用
eg:
java
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:
java
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:
java
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的类型,所有默认类型为Object
Son1 son1 = new Son1();
Object ft = son1.ft;
// Son2 定义时,未指定泛型,指定了 Father泛型为Integer,所以ft为Integer
Son2 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;
}
}
泛型接口
所谓泛型接口, 就是把泛型定义在接口上
格式:
java
interface 接口名<泛型类型1...>
举例:
java
interface Player<T,E> {
E play(T data);
}
// 在什么时候能指定它的类型?
// 1. 实现接口的时候没有指定类型,则泛型的类型为默认的Object
class YoungPlayer implements Player{
@Override
public Object play(Object data) {
return null;
}
}
// 2. 如果实现时,指定为什么类型,则是什么类型
class OldPlayer implements Player<String, Integer>{
@Override
public Integer play(String data) {
return null;
}
}
// 3. 如果子类也有泛型,则与子类一致
class BigPlayer<K,V> implements Player<K,V>{
@Override
public V play(K data) {
return null;
}
}
eg:
java
// 转换器的接口,把一个类型转换成另外一个类型
interface Converter<T, R> {
R converter(T t);
}
public class String2Date implements Converter<String, Date> {
@Override
public 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;
}
}
泛型方法
所谓泛型方法, 把泛型定义在方法上
格式:
java
<泛型类型> 返回类型 方法名(泛型类型...)
eg:
java
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以及类型强转。