泛型擦除和泛型数组
程序员文章站
2022-07-15 10:17:11
...
泛型擦除:
泛型只是在 编译期 保证对象类型相同的技术,编译后就被擦除了。真正在代码的运行期,jvm会擦除泛型的存在。(所以也可以不反编译,使用反射来验证泛型擦除!)
- 泛型擦除
- 无限制类型擦除
- 有限制类型擦除
- 泛型方法的类型擦除
- 桥接方法
1、无限制类型擦除
//无限制类型擦除
public class Orange<T>{
private T color;
public void setColor(T color) {
this.color = color;
}
public T getColor() {
return color;
}
}
反编译后
public class Orange
{
private Object color;
public Orange(){}
public void setColor(Object color)
{
this.color = color;
}
public Object getColor()
{
return color;
}
}
注:类型形参T都变成了Object
使用反射也可以验证 !
//无限制的类型擦除!
public class TypeErasure<T> {
private T name;
public T getName(){
return this.name;
}
public void setName(T name){
this.name = name;
}
public static void main(String[] args) {
TypeErasure<Integer> te = new TypeErasure<>();
TypeErasure<String> te2 = new TypeErasure<>();
System.out.println(te.getClass().getSimpleName());
System.out.println(te2.getClass().getSimpleName());
System.out.println(te.getClass() == te2.getClass()); //属于同一个类!
//利用反射获取字节码文件,看成员变量的类型!
Class<TypeErasure> clazz = TypeErasure.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
// Name:name Type:Object
System.out.println("Name:"+field.getName()+"\tType:"+field.getType().getSimpleName());
}
}
}
2、有限制类型擦除
// 有限制类型擦除
public class Watermelon<T extends Number>{
private T weight;
public T getWeight() {
return weight;
}
public void setWeight(T weight) {
this.weight = weight;
}
}
反编译后
public class Watermelon
{
private Number weight;
public Watermelon()
{
}
public Number getWeight()
{
return weight;
}
public void setWeight(Number weight)
{
this.weight = weight;
}
}
注:类型形参T都变成了上限Number
3、泛型方法的类型擦除
// 泛型方法的类型擦除
public class Peach {
public static <T> T getName(T name){
return name;
}
public static <T extends Number> T getWeight(T weight){
return weight;
}
}
反编译后
public class Peach
{
public Peach(){}
public static Object getName(Object name)
{
return name;
}
public static Number getWeight(Number weight)
{
return weight;
}
}
注:其实本质上和上面1、2点都类似!
4、桥接
// 泛型接口
public interface Info<T>{
T info(T var);
}
class InfoImple implements Info<Integer>{
@Override
public Integer info(Integer var) {
return var;
}
}
反编译后
public interface Info
{
// 无限制的类型擦除
public abstract Object info(Object obj);
}
class InfoImple implements Info
{
InfoImple(){}
public Integer info(Integer var)
{
return var;
}
public volatile Object info(Object obj)
{
return info((Integer)obj);
}
}
5、补充
public class Test {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>(Arrays.asList(1,2));
/**
* 1.当一个具有泛型信息的对象赋给另一个没有泛型信息的变量时,编译器就会丢失前者的泛型信息,这时典型的擦除
* 例子:这里intList就丢失了泛型的信息!
*
* 2.java中直接允许将List对象赋给一个List<Type>, Type是任何类型!
*/
List list = intList;
List<String> strList = list;
System.out.println(strList.get(0));//java.lang.ClassCastException
//System.out.println((String)strList.get(0)); 反编译后上一句是这样的!!
}
}
泛型数组
泛型数组的创建:
1.可以声明带泛型的数组引用。但是不能直接创建带泛型的数组对象!
(特例:java允许创建无上限的通配符泛型数组)
2.可以通过java.lang.reflect包下的Array.newInstance(Class, int) 创建T[]数组注:开发中尽量使用泛型集合来代替泛型数组
1、不能直接创建带泛型的数组对象
public class Demo {
public static void main(String[] args) {
// List<String>[] lists; // 只能声明带泛型的数组
// lists = new ArrayList<String>[5]; //(编译报错)不能直接创建带泛型的数组
// 如果真想创建泛型数组,可以这样创建(new的时候不带泛型),但是会出现编译警告!,也就是说可能发生ClassCastException
List<String>[] lists = new ArrayList[5];
// 强转成Object[]数组
Object[] objs = (Object[])lists;
List<Integer> intList = new ArrayList<>();
intList.add(20);
objs[0] = intList;
//下面代码引起ClassCastException异常
String s = lists[0].get(0);
System.out.println(s); // java.lang.ClassCastException
}
}
- 创建无上限的通配符泛型数组
public class Demo2 {
public static void main(String[] args) {
// java允许创建无上限的通配符泛型数组
List<?>[] genericArray = new ArrayList<?>[10];
Object[] objs = (Object[])genericArray;
List<String> intList = new ArrayList<>(Arrays.asList("10"));
objs[0] = intList;
//强转前应该instanceof判断下
Object target = genericArray[0].get(0);
if(target instanceof String){
String s = (String)target;
System.out.println(s+"\t"+s.getClass().getSimpleName());
}
}
}
- 不能直接对类型参数T实例化
public <T> T[] makeArray(Collection<T> c){
return new T[c.size()]; //导致编译错误, 类型参数T不能直接被实例化
}
2、通过Array.newInstance()来创建泛型数组
package cn.itcast.genericType;
import java.lang.reflect.Array;
import java.util.Arrays;
public class Fruit<T> {
// private T[] array = new T[3]; // 参数类型T不能直接被实例化!
private T[] array;
public Fruit(Class<T> clazz, int length){
// 通过Array.newInstance()创建一个泛型数组
array = (T[])Array.newInstance(clazz, length);
}
// 1.往指定位置填充元素
public void put(int index, T item){
array[index] = item;
}
// 2.获取指定位置的元素
public T get(int index){
return array[index];
}
// 3.获取数组
public T[] getArray(){
return this.array;
}
public static void main(String[] args) {
Fruit<String> fruit = new Fruit(String.class,3);
fruit.put(0,"苹果");
fruit.put(1,"香蕉");
fruit.put(2,"橘子");
System.out.println(Arrays.toString(fruit.getArray())); //[苹果, 香蕉, 橘子]
}
}
来自:虽然帅,但是菜的cxy
peace