Java泛型数组正确转换方法及类型转换异常问题排查
咱们先搞懂为什么会报错:Java的泛型是编译时特性,运行时会发生类型擦除——也就是说,你的Animal<E>在JVM运行时会被当作Animal<Object>处理。你写的(E[]) new Object[5],只是在编译时骗过了编译器,但运行时这个数组的实际类型是Object[],根本不是Food[]。
当你执行Food[] fd = (Food[]) objs时,JVM会在运行时检查数组的实际类型,发现它是Object[],和你要转的Food[]不是同一类型,自然就抛出ClassCastException了。本质上,这和你直接写(Food[]) new Object[5]会报错是一个道理。
针对这个问题,有几种靠谱的解决办法,你可以根据场景选择:
方案1:通过Class对象创建真实的泛型数组
如果必须用数组,最稳妥的方式是在构造Animal时传入E的Class对象,这样就能创建真正的E类型数组:
import java.lang.reflect.Array; public class Food { } public class Animal<E> { private E[] a; // 传入泛型类型的Class对象 public Animal(Class<E> clazz) { // 直接创建指定类型的数组 a = (E[]) Array.newInstance(clazz, 5); } public E[] returnArray() { return a; } public static void main(String[] args) { Animal<Food> animal = new Animal<>(Food.class); Food[] fd = animal.returnArray(); // 这里完全没问题 fd[0] = new Food(); } }
Array.newInstance(clazz, 5)会直接生成类型为E[]的数组,运行时的实际类型就是Food[],所以后续的赋值和使用都不会有类型转换异常。
方案2:用List替代数组(更推荐)
Java里泛型和数组的兼容性一直很差,很多时候用List<E>代替数组是更简单、更安全的选择——不需要处理烦人的类型转换,编译时就能保证类型安全:
import java.util.ArrayList; import java.util.List; public class Food { } public class Animal<E> { private List<E> list = new ArrayList<>(5); public List<E> returnList() { return list; } public static void main(String[] args) { Animal<Food> animal = new Animal<>(); List<Food> fdList = animal.returnList(); fdList.add(new Food()); } }
这种方式完全避开了数组和泛型的冲突,代码更简洁,也不容易出错。
方案3:必须用数组时,逐个转换元素而非整个数组
如果一定要保留数组的实现,那不要尝试把整个Object[]转成Food[],而是在取出元素的时候逐个转换:
public class Food { } public class Animal<E> { private E[] a = (E[]) new Object[5]; public E[] returnArray() { return a; } public static void main(String[] args) { Animal<Food> animal = new Animal<>(); Object[] objs = animal.returnArray(); // 逐个元素转换,而不是强制转换整个数组 Food foodItem = (Food) objs[0]; objs[0] = new Food(); // 这里是安全的,因为我们明确知道存的是Food类型 } }
虽然数组本身是Object[],但只要我们确保存入的元素都是E类型,逐个转换元素就是安全的,不会抛出异常。
内容的提问来源于stack exchange,提问作者Alex Zheng

