Java 泛型及相关使用
面试时遇到一个问题,泛型如何获取真实的类型?GG,下来以后查了相关资料,看了几遍才懂是啥意思,写在这里牢记!
泛型
泛型——就是将类型的明确推迟到创建对象或执行方法时再进行。泛型相当于一个参数,修饰这个类实际上是什么。
举例来说:
ArrayList
整个ArrayList
定义一个含泛型参数T的类,然后在类中设置一个T变量,和在类中直接定义一个Object类相比,有如下的好处:
- 在创建这个泛型类时,T的类型就被固定下来了,不会出现被传入错误参数的情况;
- 获取T的时候,获取到的是实际类型,而不需要再进行一次强转,以免出现强转问题。
在实例化一个泛型类时,如果不使用<>指定类型,或者使用指定类型,那么泛型参数就相当于Object,泛型类就失去了泛型的作用。 在实例化一个泛型类时,如果使用定义,可以在?后面附加条件extends A或 super A。前者表示届时这个参数化类型变量应该是A的子类,后者表示届时这个参数化类型变量应该是B的超类(祖先类)。
此外,在定义时,可以使用
泛型擦除
泛型类在编译后,<>中的类型信息会被擦除,在内存中实际表现为Object类。而在get()等方法需要返回确切类型的地方,实际的泛型参数类型会被直接写入。
例如ArrayList
E get(){/* ... */}
变成
Integer get(){/* ... */}
以ArrayList
既然有泛型擦除的概念,那么对于一个表面定型,其实不定型的ArrayList
ArrayList<Integer> ai = new ArrayList<>();
ai.add(123);
try {
ai.getClass().getDeclaredMethod("add",Object.class).invoke(ai,"string");
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
for (Object o:ai)
System.out.println(o);
for (Integer i:ai)
// 运行时会报ClassCastException,因为有一个String无法被转为Integer
System.out.println(i);
泛型获取真实类型
泛型如何获取到真实类型呢?我们分为三种情况来分析
1.某个类继承了某个泛型类型
private static class Generic<T extends Number>{
T data;
int id;
}
private static class ExtendedGeneric extends Generic<Integer>{
private void te(){
}
}
public static void main(String[] args) throws Exception {
Type t= ((ParameterizedType) ExtendedGeneric.class.
getGenericSuperclass()).getActualTypeArguments()
[0];
System.out.println(t.getTypeName());
}
运行结果:java.lang.Integer
2.某个类实现了某个泛型接口
private interface GenericInterface<P>{
P getP();
}
private static class ImplGeneric implements GenericInterface<Pipe>{
@Override
public Pipe getP() {
return null;
}
}
public static void main(String[] args) throws Exception {
Type t= ((ParameterizedType) ImplGeneric.class.
getGenericInterfaces()[0]).getActualTypeArguments()
[0];
System.out.println(t.getTypeName());
}
运行结果:java.nio.channels.Pipe
提示:这里和情况1很类似,只是父类只能有一个,接口可以有很多,所以getGenericSuperclass()返回的是一个Type,而getGenericInterfaces()返回的是一个Type[]数组。
3.某个方法的返回参数为泛型类型
这种情况下没有什么靠谱的方法,如果能确保这个泛型类型不为空,且值是统一的(不存在前述的那种反射写入其他类型的情况),可以通过不靠谱的方法得到。代码如下:
public static void main(String[] args) throws Exception {
ArrayList<String > strings = new ArrayList<>();
strings.add("123");
Class<?> c= ArrayList.class.getDeclaredMethod("get", int.class)
.invoke(strings,0).getClass();
System.out.println(c.getName());
}
运行结果:java.lang.String