目录
what's Generic?
泛型的概念:
泛型的好处:
泛型类、泛型接口、泛型方法中常用的泛型标识符的意义释义:
常用的泛型标识符说明:
自定义泛型类(常用):
自定义泛型接口(常用):
自定义泛型类、泛型接口的继承与实现:
自定义泛型方法(常用):
泛型数组以及泛型对象:
创建泛型数组 方式一:
创建泛型数组 方式2(这种方式 安全一些 ):
泛型对象:
泛型的使用细节以及注意事项(重点):
自定义泛型类及自定义接口的使用细节以及注意事项:
自定义泛型方法的使用细节以及注意事项:
泛型数组的使用细节以及注意事项:
泛型数组的使用细节:
5. 泛型数组的注意事项:
泛型对象注意事项:
泛型的通配符(常用):
泛型的上下限(常用):
泛型的上限通配:
泛型的下限通配:
关于泛型的通配符、上下限的细节说明:
泛型的通配符、上下限的使用场景:
泛型的 泛型擦除(编译时泛型擦除、运行时泛型擦除):
泛型的编译时擦除:
泛型的运行时擦除:
泛型擦除 的无限制 类型擦除:
泛型擦除 的有限制 类型擦除(即使用了 泛型的上限通配 )
泛型方法的 泛型擦除 :
泛型擦除前的桥接方法:
往 泛型指定为Double类型的List集合中添加非 Double 的实例:
:Q ---> 等同于 T 的意义。不是什么单词首字母的缩写。如果非要解释那就是 Q的字母顺序 离 T 比较近。表示为类中的泛型 "任意引用数据类型"
例如:
class Son {
private T t;
private T[] tArr;
public final List list = new ArrayList<>();
public void setT(T t) {
this.t = t;
}
public T getT() {
return t;
}
public List getList(){
return list;
}
}
test:
例如:
interface Person{
S s();
Map setMap(U u,Q q);
default void print(T t,S s){
System.out.println(t.getClass());
System.out.println(s.getClass());
}
}
test:和自定义泛型类是一样的使用,只是接口的泛型是 它的实现类在初始化时、定义时 或者是 子接口定义时、或者是 子抽象类定义时指定的!
形式1(稍微复杂些的)(不常用):
interface Person {
public abstract void personMethod(Q q);
}
// 不给定父接口泛型
interface SubPerson extends Person {
default void subMethod(C c) {
System.out.println(c.getClass());
}
}
// 给定父接口泛型,同时声明自己类中的泛型
abstract class AbstractPerson implements SubPerson, HashMap> {
@Override
public void personMethod(List list) {
list.add("抽象类向集合中添加元素");
}
public abstract ArrayList
test:
public static void testGenericExtends() {
Coder coder =
new Coder("第二个泛型为String类型",Math.E);
coder.method2(new Scanner(System.in));
// 直接以实例化时给定的泛型为准
String t = coder.getT();
System.out.println(t);
Double u = coder.getU();
System.out.println(u);
coder.personMethod(new ArrayList<>());
ArrayList objects = coder.personMethod("圆周率:", Math.PI);
System.out.println(objects);
Double result = coder.useR(1);
System.out.println(result);
}
形式2(什么泛型都不在定义泛型类、泛型接口的时候 给定)(常用):
interface People {
public abstract Integer peopleMethod(T t);
}
interface Man extends People {
@Override
Integer peopleMethod(T t);
default String m1(S s, T t) {
return s.getClass().toString() + "和" + t.getClass().toString();
}
}
abstract class Dad implements Man {
protected T t;
protected Dad(T t) {
this.t = t;
}
@Override
public Integer peopleMethod(T t) {
return (Integer) t + 999999;
}
}
class Child extends Dad {
private S s;
public Child(T t, S s) {
super(t);
this.s = s;
}
public S getS() {
return s;
}
public List extends L> getList() {
return new LinkedList<>();
}
}
test:
public static void main(String[] args) {
Child child = new Child<>(9090, "Created at 2021-06-14 08:59");
String s = child.getS();
System.out.println(s);
Integer integer = child.peopleMethod(789);
System.out.println(integer);
Integer t = child.getT();
System.out.println(t);
List super Serializable> list = child.getList();
list.add(new Date());
list.add(new java.sql.Date(System.currentTimeMillis()));
list.add(new ArrayList().add(null));
ArrayList currentTimeMillis = new ArrayList<>();
currentTimeMillis.add(new GregorianCalendar().getTime().getTime());
System.out.println(list);
System.out.println(currentTimeMillis);
System.out.println(child.m1(new String(), Integer.MAX_VALUE));
}
public class GenericTest3 {
public static void main(String[] args) {
Generic generic = new Generic<>();
Generic generic1 = generic.isNotGenericMethod(generic);
System.out.println(generic);
System.out.println(generic1);
String s = generic.isGenericMethod("是String类型");
System.out.println(s);
ArrayList dates = new ArrayList<>();
List> listInstance = Generic.getListInstance(dates);
listInstance.add(dates);
System.out.println(listInstance.getClass());
}
}
class Generic {
private T t;
public Generic(){}
// 此方法并不是泛型方法
public T isNotGenericMethod(T t) {
return isNotGenericMethod2(t);
}
// 此方法也不是泛型方法
private T isNotGenericMethod2(T t) {
T tt = null;
try {
// 通过这样的反射方式 创建本类对象,构造器必须显示初始化才可以!
tt = (T) t.getClass().getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return tt;
}
// 是泛型实例方法
public S isGenericMethod(S s){
if ("是String类型".equals(s)){
return s;
}
return null;
}
// 是泛型静态方法
public static List getListInstance(U u){
if (u instanceof List){
return new LinkedList<>();
}
return null;
}
}
result:,泛型在编译时的数据类型:class Generic2 {
private G[] gArr;
private Object obj;
public Generic2(T t) {
// 多态的引用!
obj = t;
// 创建泛型数组的方式1:
// (如果在实例化Generic2的时候,给定的泛型不是 被强转的类型时就会
// 引发并抛出 ClassCastException 异常,
// 因为向下转型的前提是要
// 父类引用指向子类对象时。
// 这时候的向下转型到子类类型的时候 才不会抛出 ClassCastException
// (G[]) new Object[5] 如果这里的向下转型在实例化Generic2的时候
// 指定的泛型如果不是Object时,是其他类的时候,就会抛出 ClassCastException异常
// 因为要清楚的知道的是 new Object[5] 这个动作是在 初始化 Object类型的
// 数组,而不是在初始化 泛型的数组,泛型的数组不能够被初始化!
// 因为泛型是未知的数据类型,怎么初始化?)
gArr = (G[]) new Object[5];
}
public G[] getgArr(){
return gArr;
}
public Object getObj(){
return obj;
}
}
test1:,使用泛型数组是,引发的ClassCastException异常情况:
class Generic2 {
private G[] gArr;
private Object obj;
public Generic2(T t) {
// 多态的引用!
obj = t;
// 创建泛型数组的方式1:
// (如果在实例化Generic2的时候,给定的泛型不会 被强转的类型时就会
// 引发并抛出 ClassCastException 异常,
// 因为向下转型的前提是要
// 父类引用指向子类对象时。
// 这时候的向下转型到子类类型的时候 才不会抛出 ClassCastException
// (G[]) new Object[5] 如果这里的向下转型在实例化Generic2的时候
// 指定的泛型如果不是Object时,是其他类的时候,
// 如果在类的外部获取 G[] 这个泛型数组,就会抛出 ClassCastException异常
// 因为要清楚的知道的是 new Object[5] 这个动作是在 初始化 Object类型的
// 数组,而不是在初始化 泛型的数组,泛型的数组不能够被初始化!
// 因为泛型是未知的数据类型,怎么初始化?)
// 如果这里用了 (G[]) new Object[5] 其本质就是在使用 Object类型的数组!
// 这种方法没有任何意义。要么就直接使用 Object 的数组,没有必要转来转去。
gArr = (G[]) new Object[5];
}
public G[] initializeGArr(Class super G> clazz, int initLength) {
// 创建泛型数组的方式2:通过反射包下( java.lang.reflect.Array;)的 Array类的newInstance创建数组对象
/* public static Object newInstance(Class> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
这个API的本质仍是返回的Object类型。)(newArray底层是一个本地方法,由C或者C++实现)
而引用数据类型的数组都继承自 Object 和 Object[]
基本数据类型的一维数组只继承自 Object,它们并没有继承自 Object[]
因为基本数据类型并不是引用数据类型!
(需要说明的是,基本数据类型
二维数组继承自 Object 、Object[]
三维数组继承自 Object 、Object[] 、 Object[][]
依次类推……
引用数据类型
二维数组继承自 Object、Object[] 、Object[][]
三维数组继承自 Object 、Object[] 、 Object[][] 、Object[][][]
依次类推……)
*/
gArr = (G[]) Array.newInstance(clazz, initLength);
return gArr;
// return (G[]) Array.newInstance(clazz, initLength);
}
public G[] getgArr() {
return gArr;
}
public Object getObj() {
return obj;
}
}
/*
使用反射创建泛型数组的对象的时候方法签名中接收Class对象时
可以有的形式:它们四种方式都有可能引发 ClassCastException!!! 不是绝对安全的!
第一种:使用 泛型通配符:
public G[] initializeGArr(Class> clazz, int initLength)
第二种:使用 泛型下限通配:
public G[] initializeGArr(Class super G> clazz, int initLength)
第三种:使用 泛型上限通配:
public G[] initializeGArr(Class extends G> clazz, int initLength)
第四种:就是不使用泛型(泛型擦除):
public G[] initializeGArr(Class clazz, int initLength)
*/
test:
public static void main(String[] args) {
Generic2> generic2 = new Generic2<>(123);
HashMap[] hashMaps = generic2.initializeGArr(HashMap.class, 3);
HashMap map = new HashMap<>();
map.put("first", "Array.newInstance(Class> componentType, int length)通过反射创建泛型数组");
hashMaps[0] = map;
for (int i = 0; i < hashMaps.length; i++) {
System.out.println(hashMaps[i]);
}
System.out.println("hashMaps.length = " + hashMaps.length);
System.out.println("hashMaps.getClass() = " + hashMaps.getClass());
Object obj = generic2.getObj();
System.out.println("obj.getClass() = " + obj.getClass());
}
public static void main(String[] args) {
Generic2 Generic2 = new Generic2<>();
Generic generic = new Generic<>();
Generic instance = Generic2.getGInstance(generic);
System.out.println(generic.toString());
System.out.println(instance.toString());
String str = Generic2.getInstance(String.class);
System.out.println(str.getClass().getSuperclass().getSimpleName());
}
// 一把都不这样创建 类的泛型的对象。
// 直接在外部使用的时候创建就好了,
// 为什么在泛型类中创建?
public G getGInstance(G g) {
return reflectNewInstance(g);
}
private G reflectNewInstance(G g) {
try {
g = (G) g.getClass().getConstructor().newInstance();
} catch (Exception e) {
}
return g;
}
// 可以使用泛型方法创建其他类的对象!
/* 前提是这个类本身就要能够创建对象*/
public T getInstance(Class c) {
T t = null;
try {
t = c.getConstructor().newInstance();
} catch (Exception e) {
}
return t;
}
public Generic2() {
}
- 不管是使用 自定义的泛型类还是用别人写的泛型类。在实例化泛型类的时候,在有 数据类型 对象名 引用的情况下。都不用再指定泛型。在JDK1.7之前需要手动指定。到时再JDK1.5之后,编译器会自行推断。比如:
- 泛型类的实例成员(实例变量、常量、方法、代码块、内部类)中可以直接使用类的泛型!
- 类的静态成员(静态变量、常量、方法、代码块、静态内部类、静态内部枚举、静态内部接口、静态内部注解)不能够使用 类的 泛型。因为泛型是属于类的实例的,静态方法是属于类的,并不是属于类的实例的。静态成员加载时还没有创建对象。所以不能够使用类的泛型。
- 泛型类在继承类的时候,这个父类不一定要是 泛型类。可以是一个普通的类。在继承、实现泛型接口的时候,这个父接口不一定要是个 泛型接口,可以是一个普通接口。
- 泛型类在继承泛型父类 or 实现接口、包括泛型子接口继承泛型父接口的时候,如果泛型类在定义的时候,没有给 泛型父类 or 泛型父接口给定具体数据类型的话,那么这个泛型类、泛型子接口 的 泛型声明 也要是和泛型父类、泛型父接口的泛型声明一样!
- 泛型类在继承泛型父类 or 实现接口的时候,泛型类在定义的时候,可以在继承 or 实现时 更改泛型父类 or 泛型父接口的 泛型标识符 ,此时泛型类的 泛型声明的泛型标识符要和自己修改的一样!(一般很少这样操作)子泛型接口 继承 父泛型接口也是一样!
- 泛型类在继承泛型父类 or 实现泛型接口的时候,泛型类在定义的时候,泛型类的泛型声明的泛型标识符的顺序可以和 泛型父类 or 泛型父接口的不一致。但是这样很容易搞混淆(基本上 不会把顺序搞得不一样。)子泛型接口 继承 父泛型接口也是一样!
- 一个普通类 也可以继承、实现 泛型父类、泛型父接口、一个 普通接口 也可以继承 泛型父接口。但是前提是 在 继承 or 实现的时候,要给 泛型父接口、泛型父类给定具体的数据类型!
- 泛型类在继承或者实现时,泛型接口在继承时。可以拓展自己的泛型声明!可以有 0 ~ n个泛型声明!
- 泛型类的构造器写法和普通类的构造器写法是一样的,泛型类的构造器后面没有<>这个符号。比如:,不会因为这个类是个泛型类,构造器的写法就有所改变。构造器如果想使用自己独有的泛型的话,那么这个构造器可以定义成为泛型构造器。
- 泛型类中的静态变量 以及 静态常量 不能使用 泛型!类中的静态成员有 静态方法、静态内部类、静态内部接口 可以使用自己的泛型!静态的成员不能够使用类的泛型!
- 如果在 定义 泛型类 的时候 没有给泛型父类和泛型父接口给定具体类型的话。那么在实例化这个 泛型类的时候,尽量给定具体的数据类型。不然就会 发生编译时的 泛型擦除。即 所有的 泛型标识符 表示的数据类型 都会泛型擦除为 Object 类型。(要么所有的泛型都不给定具体数据类型。要么全部泛型都要给定具体数据类型,没有给定几个和不给定几个的操作!)
- 泛型类的泛型是在 泛型类继承 泛型父类、实现 泛型接口 或者是 实例化泛型类的时候 给定具体数据类型。如果说 在 定义时和实例化时都没有给定具体的数据类型。那么这些泛型声明就啥作用都没有,全部泛型擦除为Object类型
- 自定义异常类不可以是泛型类,编译器不支持。但是 异常类中可以有 泛型方法。
- 自定义枚举类不可以是泛型类,编译器不支持。但是 枚举类中可以有 泛型方法。
- 自定义注解不可以是泛型注解,编译器不支持。
- 泛型方法可以分为 泛型静态方法 和 泛型实例方法 和 泛型构造器 。
- 泛型方法必须要有 <泛型标识符> 泛型声明(钻石运算符 ) !不然这个方法就不是泛型方法!如果是实例方法,那么只能说这个方法使用了类的泛型。而它本身不是泛型方法。
- 泛型方法 可以出现在 普通类、普通抽象类、普通接口、泛型类、泛型抽象类、泛型接口、枚举类、自定义异常类、静态内部类、局部内部类(只能有泛型实例方法)、实例内部类(只能有泛型实例方法)、匿名内部类(只能有泛型实例方法)
- 泛型方法的重载注意事项(不能是相同的泛型声明,这和是不是相同的泛型标识符无关,因为它们 泛型声明的 泛型擦除后的 类型都是Object,所以不构成泛型方法的重载!):,,构造泛型方法的重载就要满足方法重载的必要条件,即(方法名必须相同、参数类型不同、或者 参数类型个数不同 或者参数类型顺序不同 (和是不是静态方法、实例方法无关!)),泛型方法重载的坑:,这样 重载实例方法的时候 虽然在编译时通过了编译,倒是如果在调用的时候,传入了String类型的话。这个时候,编译器就懵逼了。比如:,引发编译不通过的错误:,解决方法:要么实例化泛型类的时候就不要指定泛型为 和已有的重载方法形参类型相同 ,要么就把 已经给定了的数据类型的那个重载方法改成其他数据类型!比如:
- 泛型方法的泛型是独立于类而存在的,类、接口的泛型是以类、接口为单位!而方法的方法是以方法为单位!即 即使泛型方法的泛型声明的泛型标识符和类、接口的泛型标识符是一样的。也是独立开来的,这并影响 泛型方法的调用。为了避免歧义。所以 Java规定了要 写 泛型声明 !
- 泛型方法的泛型声明 必须在返回值的前面、方法修饰符的后面!
- 泛型实例方法、泛型构造器 既可以使用方法、构造器 自己本身的泛型,也可以使用 类的泛型。而泛型静态方法只能够使用 泛型静态方法自己本身 的泛型!
- 泛型方法的泛型是在调用方法时 给定具体的 数据类型 的,即调用泛型方法时,传入了 什么引用数据类型的实例(即 实参)调用方法,即 这个泛型表示符 所表示的 数据类型就是 这个实参的 数据类型。
- 而 (泛型类泛型接口的泛型 可以是在 类、泛型类 继承 泛型父类,实现泛型父接口的时候 给泛型父类、泛型父接口 给定具体的数据类型)(一般都不会在继承、实现是给定 引用数据类型,只是说可以zheyan)。如果没有给定,那么这个泛型类的 泛型标识也要和 泛型父类、泛型父接口的泛型标识一样。如果没有在继承 or 实现的时候给定 泛型父类、泛型父接口具体的数据类型。那么在实例化 泛型类 的时候,也可以 给定 泛型的具体类型!
创建泛型数组的方式2:通过反射包下( java.lang.reflect.Array;)的 Array类的newInstance创建数组对象, public static Object newInstance(Class> componentType, int length){return newArray(componentType, length);} 这个API的本质仍是返回的Object类型。)(newArray底层是一个本地方法,由C或者C++实现);
关于数组的继承关系:引用数据类型的一维数组都继承自 Object 和 Object[],基本数据类型的一维数组只隐式继承自 Object,它们并没有隐式继承自 Object[],因为基本数据类型并不是引用数据类型! (需要说明的是,基本数据类型
二维数组隐式继承自 Object 、Object[]
三维数组隐式继承自 Object 、Object[] 、 Object[][]依次类推……
引用数据类型
二维数组隐式继承自 Object、Object[] 、Object[][]
三维数组隐式继承自 Object 、Object[] 、 Object[][] 、Object[][][]
依次类推……)
,但是这种方式缺点太明显了。明明初始化 给定的是char[] 因为的类型。但是在调用这个方法的时候却要传入一个 char类型的 char[][] 二维数组。那是因为 U[] 这个声明又在 char[] 这个一维数组又声明了一维,所以才变成了二维数组。因为要明确的是 只有 char[] 这个一维数组类型才是隐式 继承了 Object,所以编译通过,而 char 这个类型本身就是基本数据类型。它并没有隐式继承 Object!。所以为什么是 给定的是char[] 类型,但是 却要传入 一个 char[][] 的二维数组才能使用!所以如果想要使用 char[] 类型的 的数组的话,只能使用它的 包装类型!
,这样使用数组的话 就不会造成明明给定的是 一维数组 但是使用的时候却要二位数组才能使用。比如:,需要特别注意的是。这里的 给定具体的数据类型的时候 。只需要给定 类型 即可。不要再在后面声明数组的形式传入进去!如果在后面声明数组的话,那么在操作的时候,仍然是 要传入给定类型的 二维数组才能操作! 依次类推!!!
public G[] initializeGArr(Class super G> clazz, int initLength)
如果是 这种 泛型通配上限 的 这种情况的话 :可以传入自己的 类对象,以及父类的类对象。比如:
(可以拿自己的类型来接收或者拿父类的类型 多态接收)。
如果是传入的父类的 类对象的话,就必须要 拿 父类自己本身的类型 或者 父类的父类的类型 多态接收。
public G[] initializeGArr(Class extends G> clazz, int initLength) :
如果是这种 泛型下限通配 的情况的话,可以传入自己的类对象,以及所有子类的类对象。
接收类型可以是子类类型本身(强制类型转换),也可以父类类型 多态引用!
如果是子类的类型和 其他父类类型 引用的时候,就要进行类型的 强制转换!
因为在编译时,编译器就一直会以 给定的 数据类型 为父类,从而 一直以多态的方式 指向 子类 对象。
而运行时泛型擦除后 是以 Object 来指向的。
和泛型数组一样的。一般都不会在泛型类中 去创建一个泛型的对象。 我都不知道什么类型?怎么创建?
如果非要创建这个泛型对象不可的话,和泛型数组的注意事项是一样的,尽量通过 泛型的上下限 让泛型受限。尽量通过反射来实例化对象。应该尽量避免出现 ClassCastException 异常 和其他一些 可能发生 的异常。
class Created {
private T t;
public Created(T t) {
this.t = t;
}
public static void main(String[] args) {
List oList = new LinkedList<>();
oList.add(LocalDateTime.now(ZoneId.of("UTC+8")));
List sList = new ArrayList<>();
sList.add("String");
List iList = new Vector<>();
iList.add(2);
List dVector = new Vector<>();
dVector.add(3.1415);
Map hashMap = new HashMap<>();
hashMap.put("牛郎","织女");
Map linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("husband","wife");
Map hashMap1 = new HashMap<>();
hashMap1.put("boy","girl");
Map treeMap = new TreeMap<>();
treeMap.put("male","female");
invokeEachList(oList, hashMap);
invokeEachList(sList, linkedHashMap);
invokeEachList(iList, hashMap1);
invokeEachList(dVector, treeMap);
}
private T getT() {
return t;
}
public static void invokeEachList(List> list, Map, ?> map) {
Created created = new Created<>("执行");
created.eachList(list).eachMap(map, true, created.getT());
}
public Created eachList(List> list) {
// 可以直接对 list 进行遍历查看,
// (接收的数据类型是Object)
for (Object o : list) {
}
// 可以获取 list 集合中的元素
// (返回的数据类型是Object)
// 如果集合中没有元素 就会引发: IndexOutOfBoundsException 异常!
Object o = list.get(0);
System.out.println(o);
// 可以添加、设置 null 值到集合中。但这没有任何的意义
list.add(null);
System.out.println(list);
list.set(0, null);
System.out.println(list);
// 不可添加其他引用数据类型的 实例。因为 ? 通配符表示是未知的不可以预料的。
// 假如可以 添加一个字符串 对象到 list 集合中,
// 那万一调用方法时,传入的集合的泛型的数据类型是 Integer 呢?
// 那不是类型不匹配,自相矛盾,违背了 泛型的 限制要求、监测机制吗?
// 可以删除 list 集合中的元素,前提是集合要有这个索引的元素,
// 不然抛出 IndexOutOfBoundsException
System.out.println("Objects.isNull(list.remove(0)) = " + Objects.isNull(list.remove(0)) + "\n");
return this;
}
public void eachMap(Map, ?> map, C c, T t) {
if (c.equals(true) && "执行".equals(t)) {
// 可以对Map集合遍历查看元素
for (Map.Entry, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + "==" + entry.getValue());
}
try {
// 可以对Map集合添加、设置 null值到Map中(但这没有任何的意义!)
map.put(null, null);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("map = " + map.getClass().getSimpleName() + map);
// 可以 获取 Map集合中的键值对
System.out.println("map.get(\"1\") = " + map.get("1"));
// 可以删除Map集合中的键值对(前提是要有这个ke
System.out.println("map.remove(\"1\") = " + map.remove("1") + "\n");
}
}
}
}
test:class Underground extends Floor {
public void m(List super Floor> list) {
}
public void unMethod(U u, T t) {
super.method(u, t);
}
}
class Floor extends Center {
public void method(S s, T t) {
System.out.println(s);
System.out.println(t);
}
}
class Center extends Fly implements Comparable {
@Override
public int compareTo(@NotNull Center o) {
return 1;
}
}
class Fly {
}
public class GenericErasureTest {
public static void main(String[] args) {
RuntimeErasure erasure = new RuntimeErasure<>();
erasure.setInfo("什么是快乐星球?");
// info 这个字段的运行 类型是 String 。
System.out.println(" info 这个字段的运行 类型是 : " + erasure.getInfo().getClass());
// 通过反射 查看 RuntimeErasure 这个类的泛型 泛型擦除后类型
Class extends RuntimeErasure> clazz = erasure.getClass();
// 得到 字段实例 数组
Field[] fields = clazz.getDeclaredFields();
// 得到 方法实例 数组
Method[] methods = clazz.getDeclaredMethods();
for (Field field : fields) {
System.out.println("字段名字:" + field.getName()
+ ", 泛型擦除后 字段类型:" + field.getType());
}
for (Method method : methods) {
System.out.println("方法名字:" + method.getName() +
", 泛型擦除后 返回值类型:" + method.getReturnType());
for (Parameter parameter : method.getParameters()) {
System.out.println("参数名字 :" + parameter.getName()
+", 泛型擦除后 参数类型:" + parameter.getType());
}
}
}
}
public static void erasure() {
RuntimeErasure erasure = new RuntimeErasure<>();
erasure.setFiled(6666,"有限制泛型擦除");
// info 这个字段的运行 类型是 String 。
System.out.println(" info 这个字段的运行 类型是 : " + erasure.getInfo().getClass());
System.out.println(" str 这个字段的运行 类型是 : " + erasure.getStr().getClass());
// 通过反射 查看 RuntimeErasure 这个类的泛型 泛型擦除后类型
Class extends RuntimeErasure> clazz = erasure.getClass();
// 得到 字段实例 数组
Field[] fields = clazz.getDeclaredFields();
// 得到 方法实例 数组
Method[] methods = clazz.getDeclaredMethods();
for (Field field : fields) {
System.out.println("字段名字:" + field.getName()
+ ", 泛型擦除后 字段类型:" + field.getType());
}
for (Method method : methods) {
System.out.println("方法名字:" + method.getName() +
", 泛型擦除后 返回值类型:" + method.getReturnType());
for (Parameter parameter : method.getParameters()) {
System.out.println("参数名字 :" + parameter.getName()
+ ", 泛型擦除后 参数类型:" + parameter.getType());
}
}
class RuntimeErasure {
private R info;
private U str;
public R getInfo() {
return info;
}
public U getStr() {
return str;
}
public void setFiled(R info, U str) {
this.info = info;
this.str = str;
}
}
public static void erasureMethod() {
RuntimeErasure erasure = new RuntimeErasure<>();
erasure.setInfo("泛型擦除");
erasure.setMap(new HashMap<>());
// info 这个字段的运行 类型是 String 。
System.out.println(" info 这个字段的运行 类型是 : " + erasure.getInfo().getClass());
// map 这个字段的运行 类型是 HashMap
System.out.println(" map 这个字段的运行 类型是 : " + erasure.getMap().getClass());
Class extends RuntimeErasure> clazz = erasure.getClass();
Field[] fields = clazz.getDeclaredFields();
Method[] methods = clazz.getDeclaredMethods();
for (Field field : fields) {
System.out.println("字段名字:" + field.getName()
+ ", 泛型擦除后 字段类型:" + field.getType());
}
for (Method method : methods) {
System.out.println("方法名字:" + method.getName() +
", 泛型擦除后 返回值类型:" + method.getReturnType());
for (Parameter parameter : method.getParameters()) {
System.out.println("参数名字 :" + parameter.getName()
+ ", 泛型擦除后 参数类型:" + parameter.getType());
}
}
}
}
class RuntimeErasure {
private R map;
private U info;
public U getInfo() {
return info;
}
public R getMap() {
return map;
}
public void setInfo(U info) {
this.info = info;
}
public void setMap(R map) {
this.map = map;
}
public S instanceGeneric(S value) {
return value;
}
public static T staticGenricMethod(T value) {
return value;
}
public A instanceGeneric(A a) {
return a;
}
}
说明:这种关系 只存在于 实现类与泛型接口 、子类与泛型抽象父类 中。这个操作过程 由 编译器完成。
发生这种桥接的前提有三种情况:(跟子类、实现类、子接口是不是 泛型类、泛型接口 无关,生成桥接方法的前提就是,子类、实现类、子接口 在继承、实现时就给定了具体的数据类型!(说明:如果给定的泛型的 引用数据类型是 Object 的话,就不存在 泛型擦除前的桥接方法 !))
第一种:实现类 与 泛型父接口,即泛型父接口的 泛型在实现类 实现的时候 就已经给定了具体的数据类型。这个时候就会有个 桥接方法在 实现类的 class 文件中!比如:
public static void bridging() {
Class> clazz = null;
try {
clazz = Class.forName("indi.jonny.exercises.generic.BridgingImpl");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("方法名字:" + method.getName() +
", 方法返回值类型: " + method.getReturnType());
for (Parameter parameter : method.getParameters()) {
System.out.println("参数名字:" + parameter.getName() +
",参数类型:" + parameter.getType());
}
}
}
interface Bridging{
public abstract T bri(T value);
}
class BridgingImpl implements Bridging{
@Override
public String bri(String value) {
return value;
}
}
result:,这个桥接方法存在的意义就在于,保持泛型父接口和实现类的实现关系。反编译查看字节码文件:,可以理解成为 实际调用 bri 这个方法的时候 实际调用的是 桥接的这个方法,只是在桥接方法中虚拟机又调用 实现类中重写的方法,它的存在表现形式可以理解为:
第二种:子类 继承自 泛型抽象父类 。即泛型抽象父类的 泛型在 子类 继承的时候 就已经给定了具体的数据类型。这个时候就会有个 桥接方法在 子类的 class 文件中!比如:
public static void bridging() {
Class> clazz = null;
try {
clazz = Class.forName("indi.jonny.exercises.generic.SubBridging");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("方法名字:" + method.getName() +
", 方法返回值类型: " + method.getReturnType());
for (Parameter parameter : method.getParameters()) {
System.out.println("参数名字:" + parameter.getName() +
",参数类型:" + parameter.getType());
}
}
}
abstract class AbstractBridging{
public abstract T bri(T value);
}
class SubBridging extends AbstractBridging {
@Override
public Integer bri(Integer value) {
return value;
}
}
result:,这个桥接方法存在的意义就在于,保持泛型抽象父类和 子类的继承关系。反编译查看字节码文件:,可以理解成为 实际调用 bri 这个方法的时候 实际调用的是 桥接的这个方法,只是在桥接方法中虚拟机又调用 实现类中重写的方法,它的存在表现形式可以理解为:
第三种:子接口 继承自 泛型父接口 。即泛型父接口的 泛型在 子接口 继承的时候 就已经给定了具体的数据类型。这个时候就会有个 桥接方法在 子接口的 class 文件中!比如:
public static void bridging() {
Class> clazz = null;
try {
clazz = Class.forName("indi.jonny.exercises.generic.SubBridgingInterface");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println("方法名字:" + method.getName() +
", 方法返回值类型: " + method.getReturnType());
for (Parameter parameter : method.getParameters()) {
System.out.println("参数名字:" + parameter.getName() +
",参数类型:" + parameter.getType());
}
}
}
interface BridgingInterface {
T bri(T value);
}
interface SubBridgingInterface extends BridgingInterface {
@Override
Double bri(Double value);
}
test:,这个桥接方法存在的意义就在于,保持泛型父接口 和 子接口 的继承关系。反编译查看字节码文件:,在实现类实现这个子接口的时候,这个桥接方法就会被 继承下去!比如:
public static void main(String[] args) throws Throwable {
class Temp {
private T info;
private R other;
public Temp(T info, R other) {
this.info = info;
this.other = other;
}
@Override
public String toString() {
return "Temp{" +
"info=" + info +
", other=" + other.getClass() +
'}';
}
}
LinkedList doubleList = new LinkedList<>();
doubleList.add(Math.PI);
doubleList.add(Math.E);
System.out.println(doubleList);
// 拿到LinkedList的 add 方法对象。
Class extends LinkedList> linkedClass = doubleList.getClass();
// 泛型擦除后,(除了有限制泛型擦除外,)
// 所以在泛型类中、泛型接口中、泛型方法中的泛型标识符 都为 Object类型
Method add = linkedClass.getMethod("add", Object.class);
add.invoke(doubleList,"泛型擦除后的第一个字符串");
add.invoke(doubleList,new Temp
("泛型擦除后的第一个对象",
BigInteger.valueOf(System.currentTimeMillis())));
System.out.println(doubleList);
}
下一篇:17 :Collection集合接口:List 集合子接口下的 ArrayList 实现类集合的基本使用