目录
泛型类
定义:泛型指的是类定义的时候,并不会设置类中的属性或方法中参数的具体类型,而是在类使用时再定义。
语法:
class MyClass<T>{
T value1;
}
T被称为类型参数,用于指代任何类型。实际上这个T你可以任意写,但出于规范的目的,Java还是建议我们用单个大写字母来代表类型参数,常见的有:
T:代表一般的类。
E:代表element,常用于泛型类中的属性。
K:代表Key的意思。
V:代表Value的意思,通常与K一起配合使用。
泛型类的使用:
class MyClass<T>{
private T value1;
public T getValue1() {
return value1;
}
public void setValue1(T value1) {
this.value1 = value1;
}
}
public class Test{
public static void main(String[] args) {
MyClass<String> myClass = new MyClass<>();
myClass.setValue1("hello");
System.out.println(myClass.getValue1());
MyClass<Integer> myClass1 = new MyClass<>();
myClass1.setValue1(10);
System.out.println(myClass1.getValue1());
}
}
注意:泛型只允许接收类,所有基本类型必须使用包装类。
如果在类中需要定义多个属性,并且属性的类型不同,可以在类中写多个类型参数。
泛型方法
例:
public <T> T MyMethod(T t){
System.out.println(t);
}
注意:泛型可以单独定义方法,即便所在的类不是泛型类,也可以单独定义方法。
<T>只是告诉编译器这是泛型方法,并不是返回值。第一个T才是返回值。
我们来看下面的一个问题:
在泛型类中使用泛型方法,思考一下,我圈出来的三个T是一个东西吗?
在泛型类中,我们知道,泛型类的T和属性中的T一定是同一个东西,那么泛型方法是否和他们一样呢?
我们来测试一下:
public class Test{
public static void main(String[] args) {
MyClass<String> myClass = new MyClass<>();
myClass.method("hello");
myClass.method(1234);
}
}
运行结果:
结论:当泛型类与泛型方法共存时,泛型类中的泛型参数与泛型方法中的类型参数没有关系,泛型方法始终以自己定义的类型参数为准。
规范:泛型方法类型参数与泛型类的类型参数不要同名。
通配符
1. ? --- 表示可以接收任意类型
用于方法中,表示参数可以接收任意类型的泛型类。
class MyClass<T>{
private T value1;
public T getValue1() {
return value1;
}
public void setValue1(T value1) {
this.value1 = value1;
}
}
public class Test{
public static void main(String[] args) {
MyClass<String> myClass = new MyClass<>();
myClass.setValue1("world");
print(myClass);
}
public static void print(MyClass<?> myClass){
System.out.println(myClass.getValue1());
}
}
注意:只能取得类中数据,不能修改数据,因为类型不确定,无法设置确定类型。
2. ? extends 类:设置/取得泛型上限
eg:? extends Number:表示泛型必须是Number及其子类。
用在类上 T extends 类:T必须为类或者类的子类。
用在方法上 ? extends 类:只能接收类或者其子类的泛型类。
注:只能取得类中属性,不能修改值(发生父类到子类的向下转型,需要强转,由于子类不确定,因此无法转型)
3. ? super 类:取得泛型下限 --- 只能用于方法中
eg:? super String:表示此方法只能取得String及其父类Object
可以设置属性值(子类到父类是自动的向上转型)。
泛型接口
interface IInterface<T>{
T test(T t);
}
子类有两种实现方式:
//1.子类继续保留泛型
class InterfaceImpl<T> implements IInterface<T>{}
//2.子类定义时确定好类型
class InterfaceImpl implements IInterface<String>{}
类型擦除
在此之前,我们先提出一个概念。
语法糖:仅存在于源码阶段,编译后就消失不见。作用是为了方便程序员的开发。
Java中典型的语法糖有泛型、自动拆装箱等。
泛型信息仅存在于代码的编译阶段,进入JVM之前,与泛型相关的信息会被擦除掉,专业术语:类型擦除。
换句话说,泛型类与普通类在Java虚拟机内没有任何差别。
举例说明:
class MyClass<T>{
T t;
}
public class Test{
public static void main(String[] args) {
MyClass<String> myClass = new MyClass<>();
MyClass<Integer> myClass1 = new MyClass<>();
System.out.println(myClass.getClass() == myClass1.getClass());
}
}
不管类型参数是String还是Integer,都会被擦除,最后还是同一个东西。
泛型类进入JVM之前会进行类型擦除,之前泛型类的类型参数若没有指定上限,会被擦除成为Object类型。如果指定上限,则参数类型被替换为相应类型上限。