关于JAVA泛型数组类型擦除引发的问题及解决方案

Posted 梦在旅途

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于JAVA泛型数组类型擦除引发的问题及解决方案相关的知识,希望对你有一定的参考价值。

先看如下一个DEMO示例代码:(其中doBatchGet被子类重写了1次)

public abstract class BaseDemoService<T> 

    public String batchGet(T... ints) 
        T one=ints[0];
        System.out.println("one class:" + one.getClass().getCanonicalName());
        return doBatchGet(one);

    

    public String doBatchGet(T... ints)
        System.out.println("com.example.springwebapp.service.Demo1Service.doBatchGet");
        return String.join(",", Arrays.toString(ints));
    
    
    public String doGet(T one)
        return one.toString();
    

    
@Service
public class Demo1Service extends BaseDemoService<Integer> 

    @Override
    public String doBatchGet(Integer... ints) 
        System.out.println("com.example.springwebapp.service.Demo1Service.doBatchGet");
        return super.doBatchGet(ints);
    


    @Override
    public String doGet(Integer one) 
        return super.doGet(one);
    

    

执行单元测试,示例代码如下:

    @Resource
    private BaseDemoService baseDemoService;

    @Test
    public void testOverride()
        Integer[] ints=1,2,3,4,5,6;
        System.out.println("batchGet result:" + baseDemoService.batchGet(ints));

    

大家认为最后batchGet result输出的结果如何?有人可能会认为这太简单了,就是1,但实际上的结果可能会让你打脸,居然直接报错,如下所示:

one class:java.lang.Integer

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;

	at com.example.springwebapp.service.Demo1Service.doBatchGet(Demo1Service.java:5)
	at com.example.springwebapp.service.BaseDemoService.batchGet(BaseDemoService.java:11)
	at com.example.springwebapp.Springwebapp1ApplicationTests.testOverride(Springwebapp1ApplicationTests.java:38)
... ...

报错的意思是类型转换错误,object数组类型不能转换为integer数组类型,但我上面一行明明输出的结果就是one class:java.lang.Integer,为何传给doBatchGet就变成了object类型呢?是不是有点看不懂了,编译也是正常的,但运行就是报错,如果把入参的ints直接传到doBatchGet方法入参中也是OK的,或者内部将doBatchGet改为doGet,即使子类重写doGet一样可以正确获取结果 又或者doBatchGet没有被子类重写也可以正确获得结果,但如果单独从ints中取一个成员然后再调doBatchGet方法就会报错,大家知道为啥吗?

我这里就不卖关子了,其实就是泛型参数类型擦除“惹的祸”,因为别看我们是BaseDemoService,其实这个T在编译后就是一个Object,在从ints取出一个成员值one后,虽然指明是T,但实际就是Object,最后在调doBatchGet的时候,JVM会将one这个变量隐式转换为one的Object数组,然后调doBatchGet,由于doBatchGet方法被子类重写,而重写后入参是具体的类型(Integer数组),这样就导致了上面的object数组类型不能转换为integer数组类型的错误。

当我们知道了问题原因后,那么就可以对症下药了.

解法一(关键点:通过反射创建一个入参真实类型的数组,这个数组与doBatchGet入参类型一致):

    public String batchGet(T... ints) 
        T[] arr = (T[]) Array.newInstance(ints[0].getClass(), 1);
        arr[0]=ints[0];
        System.out.println("arr class:" + arr.getClass().getCanonicalName());
        return doBatchGet(arr);
    

解法二(关键点:将doBatchGet由原来的可变参数直接改为List,这样即使子类重写,因为List本身就是泛型,所以擦除了就都是一样的):

    public String batchGet(T... ints) 
        return doBatchGet(Arrays.asList(ints));
    

public String doBatchGet(List<T> ints)
        System.out.println("com.example.springwebapp.service.Demo1Service.doBatchGet");
        return ints.toString();
    

以上就是实际项目中碰到的问题及解法,希望对大家也有帮助。

以上是关于关于JAVA泛型数组类型擦除引发的问题及解决方案的主要内容,如果未能解决你的问题,请参考以下文章

泛型 Generic 类型擦除引起的问题及解决方法

java泛型泛型的内部原理:类型擦除以及类型擦除带来的问题

java泛型 泛型的内部原理:类型擦除以及类型擦除带来的问题

java从toArray返回Object[]到泛型的类型擦除

泛型的内部原理:类型擦除以及类型擦除带来的问题

从头认识java-13.11 对照数组与泛型容器,观察类型擦除给泛型容器带来什么问题?