简体   繁体   English

Java通用转换错误

[英]Java Generic Cast Error

I've got an issue in java that I don't understand, could someone explain me that strange behaviour? 我在Java中有一个我不了解的问题,有人可以向我解释这种奇怪的行为吗?

my code : 我的代码:

package com.test;

import junit.framework.TestCase;

public class MyTest extends TestCase{

    class Container<I, J extends I> {
        protected J data;

        public J getData() {
            return data;
        }

        @SuppressWarnings("unchecked")
        public void setData(I data) {
            try {
                this.data = (J) data;
            } catch (ClassCastException e) {
                System.err.println("Cast" + e);
            }
        }
    }

    class A {
        public String a = "A";
    }

    class B extends A {
        public String B = "B";
    }

    class C extends A {
        public String C = "C";
    }

    public void test1() throws Exception{
        Container<A, B> container = new Container<>();
        container.setData(new C());
        assertNull(container.getData());
    }
}

I expected this test to pass, but I've got this following error : 我希望该测试能够通过,但是出现以下错误:

junit.framework.AssertionFailedError: Expected: <null> but was: com.test.MyTest$C@5c228bbd
    at junit.framework.Assert.fail(Assert.java:57)
    at junit.framework.Assert.assertTrue(Assert.java:22)
    at junit.framework.Assert.assertNull(Assert.java:277)
    at junit.framework.Assert.assertNull(Assert.java:268)
    at junit.framework.TestCase.assertNull(TestCase.java:438)
    at com.test.MyTest.test1(MyTest.java:39)

How is it possible that the container can contain a C class into a B class? 容器怎么可能将C类包含为B类?

Also, if I try to get the B value from the data, I've got a ClassCastException... 另外,如果我尝试从数据中获取B值,则会收到ClassCastException ...

public void test1() throws Exception{
    Container<A, B> container = new Container<>();
    container.setData(new C());
    System.out.println(container.getData().B);
}

Executing that test gives this following error : 执行该测试会出现以下错误:

java.lang.ClassCastException: com.test.MyTest$C cannot be cast to com.test.MyTest$B
    at com.test.MyTest.test1(MyTest.java:39)

You shouldn't suppress compile-time warnings, otherwise you cannot be surprised that your code has a strange runtime behaviour. 您不应抑制编译时警告,否则您的代码具有奇怪的运行时行为就不会感到惊讶。 Your cast in setData is unchecked because of type erasure, it means it will never fail. 由于类型擦除,您对setData的强制转换未选中,这意味着它永远不会失败。 If you want a J to be passed, just use J in the signature instead of I . 如果要传递J ,只需在签名中使用J而不是I The type consistency will be ensured compile-time. 类型一致性将在编译时得到保证。

By the way, assertNull was not the right way to test this behaviour. 顺便说一句, assertNull不是测试此行为的正确方法。 You should have let the ClassCastException propagate and use JUnit annotations : 您应该让ClassCastException传播并使用JUnit批注:

@Test(expected = ClassCastException.class)
public void test1() {
    Container<A, B> container = new Container<>();
    container.setData(new C());
}

This is because of type erasure . 这是因为类型擦除 The generic constaints are only for compile-time checks (which your code passes). 通用内容仅用于编译时检查(您的代码通过了)。 Once your code is compiled, the I and J generic types are substituted for Object in the resulting code. 编译代码后,将在生成的代码中将IJ泛型类型替换为Object

So the running code really looks like: 因此,正在运行的代码确实看起来像:

public void setData(Object data) {
    try {
        this.data = (Object) data; //Obviously always works!
    } catch (ClassCastException e) {
        System.err.println("Cast" + e);  //This line never reached
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM