本文最后更新于:October 23, 2022 pm
Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。
目录
泛型类
泛型类,在创建对象的时候才指定(或确定)其具体类型。
常用泛型标识:T(type。常用与类、接口)、E(element。常用与Collection集合)、K、V(K和V通常配合使用,常用语KV键值对类型)。
⚠️注意:这只是一种标识,就和方法参数一样。所以,任意字母都是可以的,只是习惯性用这几种来表示而已。
定义语法
| class 类名称 <泛型标识,泛型标识,...>{ private 泛型标识 变量名; }
|
示例
这里特意将Setter/Getter都显示的写了出来。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.tothefor.motorcode.Dro;
import com.alibaba.fastjson.JSON;
public class JavaGeneric<T> { private T data;
public JavaGeneric(T data) { this.data = data; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public void showData(T data) { System.out.println(JSON.toJSONString(this.data)); System.out.println(JSON.toJSONString(data)); } }
|
测试使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package com.tothefor.motorcode.Dro;
public class TestDro { public static void main(String[] args) { JavaGeneric<String> dro = new JavaGeneric<>("墨水记忆"); System.out.println(dro.getData()); dro.showData("www.tothefor.com"); } }
|
输出
| 墨水记忆 "墨水记忆" "www.tothefor.com"
|
特殊
也可以有多个类型:public class JavaGeneric< T,K >。
特殊情况:当泛型类没有指定具体类型时,将按照Object类型进行。
不支持基本数据类型,只支持引用类型。因为基本数据类型不是继承自Object。而所有的引用类型都是继承自Object。
重要知识点
泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型。
即:同一泛型类,根据不同的数据类型创建的对象,本质上是同一类型,均为泛型类。
如:public class JavaGeneric< T >,则不管是创建的 JavaGeneric<String> 对象,还是创建的 JavaGeneric<Integer> 对象,他们本质上都是 JavaGeneric 类型。如下证明:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.tothefor.motorcode.Dro;
public class TestDro { public static void main(String[] args) { JavaGeneric<String> dro = new JavaGeneric<>("墨水记忆"); JavaGeneric<Integer> droI = new JavaGeneric<>(777); System.out.println(dro.getClass()); System.out.println(droI.getClass()); System.out.println(dro.getClass() == droI.getClass()); } }
|
输出结果为:
| class com.tothefor.motorcode.Dro.JavaGeneric class com.tothefor.motorcode.Dro.JavaGeneric true
|
子类继承
分为两种情况:子类也是泛型类、子类不是泛型类。
- 子类也是泛型类,则子类和父类的泛型类型要一致。因为创建子类前一定会创建父类。(调用父类的构造方法)
| class childGeneric<T> extends Generic<T>
|
示例:
JavaGeneric(父类):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.tothefor.motorcode.Dro;
import com.alibaba.fastjson.JSON;
public class JavaGeneric<E> { private E data; public E getData() { return data; }
public void setData(E data) { this.data = data; } }
|
SonJavaGeneric(子类):
当子类有类型,父类没有类型,则默认父类为Object。可以重写父类的Getter方法查看返回值。
| public class SonJavaGeneric<T> extends JavaGeneric{ @Override public Object getData() { return super.getData(); } }
public class SonJavaGeneric<T> extends JavaGeneric<T>{ @Override public T getData() { return super.getData(); } }
|
子类多类型,如:public class SonJavaGeneric< T,K,V > extends JavaGeneric< T > ,这样也是可以的,在子类中就可以使用多种类型。但是,必须要保证有一个和父类的类型保持一致。
- 子类不是泛型类,则父类要明确泛型的数据类型。这时,子类就和正常的类没有什么区别。
| class childGeneric extends Generic<String>
|
泛型接口
和泛型类类似。
定义语法
| interface 接口名称 <泛型标识,泛型标识, ...>{ 泛型标识 方法名(); }
|
泛型接口的使用
Generic泛型接口:
| package com.tothefor.motorcode.Dro;
public interface Generic<T> { T getKey(); }
|
有两种情况:实现类也是泛型类、实现类不是泛型类。
| package com.tothefor.motorcode.Dro;
public class GenericImpl<T> implements Generic<T>{ @Override public T getKey() { return null; } }
|
使用类似泛型类,比如多类型。
| package com.tothefor.motorcode.Dro;
public class GenericImpl implements Generic<String>{ @Override public String getKey() { return null; } }
|
通泛型类一样,如果不写泛型接口的类型,则默认为Object。
泛型方法
定义语法
| 修饰符 <T,E, ...> 返回值 方法名称(形参列表){ ... }
public <T> void show(T data){ ... }
|
- public 与返回值中间的 < T >
非常重要
。可以理解为是声明此方法为泛型方法。
- < T > 表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
- 只有声明了 < T > 的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
类型通配符
类型通配符:一般是使用问号(?)代替具体的类型实参。所以,类型通配符是类型实参
,而不是类型形参。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.tothefor.motorcode.Dro;
public class Box<T> { private T first;
public T getFirst() { return first; }
public void setFirst(T first) { this.first = first; } }
|
使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package com.tothefor.motorcode.Dro;
import com.alibaba.fastjson.JSON;
public class TestDro { public static void main(String[] args) { Box<Number> boxStr = new Box<>(); boxStr.setFirst(111); showBox(boxStr);
Box<Integer> boxInt = new Box<>(); boxInt.setFirst(777); showBox(boxInt); }
public static void showBox(Box<Number> box) { Number first = box.getFirst(); System.out.println(first); } }
|
可能有人为了解决这个报错问题,会想通过重载再定义一个showBox(Box<Number> box)方法。但是,这是错误的。前面就已经说过了泛型类不同的参数类型,但是本质还是泛型类的类型,所以,都是Box类型。
解决办法:
| public static void showBox(Box<?> box) { Object first = box.getFirst(); System.out.println(first); }
|
问号(?)表示可以传入任何类型。也因为没有指定类型,所有默认为Object。
类型通配符的上限
即规定传入类型的上限,有一个规定的范围。而不再是任何类型。
语法
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。即实参类型为最大父类型,传入的可以是此类型以及此类型的子类。
上面的解决办法就可以改为:
| public static void showBox(Box<? extends Number> box) { Number first = box.getFirst(); System.out.println(first); }
|
不知道这两种解决办法你看出来区别没有。可以看见,类型不再是Object了,而是我们规定的上限类型。这里又有点多态的感觉:父类引用指向子类对象。
类型通配符的下限
即规定传入类型的下限,有一个规定的范围。而不再是任何类型。
语法
要求该泛型的类型,只能是实参类型,或实参类型的父类类型。刚好和上限反过来了。
类型擦除
类型下限同理。
泛型与数组
泛型数组的创建
- 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象。如下所示。
| ArrayList<String>[] listArr = new ArrayList[2]; ArrayList<String>[] listArr2 = new ArrayList<String>[2];
|
- 可以通过java.lang.reflect.Array的newInstance(Class<T>,int)创建T[] 数组。
泛型与反射
反射常用的泛型类:Class<T>、Constructor<T>。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.tothefor.motorcode.Dro;
import com.alibaba.fastjson.JSON;
import java.lang.reflect.Constructor; import java.util.ArrayList;
public class TestDro { public static void main(String[] args) throws Exception{ Class<Box> boxClass = Box.class; Constructor<Box> constructor = boxClass.getConstructor(); Box box = constructor.newInstance(); } }
|