Java Generic Cast Error

时间:2015-10-07 14:34:19

标签: java

我在java中遇到了一个我不明白的问题,有人可以解释我这种奇怪的行为吗?

我的代码:

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());
    }
}

我希望这个测试通过,但我有以下错误:

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)

容器如何可以将C类包含在B类中?

另外,如果我尝试从数据中获取B值,我就得到了ClassCastException ......

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

执行该测试会出现以下错误:

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

2 个答案:

答案 0 :(得分:3)

您不应该禁止编译时警告,否则您的代码具有奇怪的运行时行为就不会感到惊讶。由于类型擦除,您setData中的广告素材未被选中,这意味着永远不会失败。如果您希望传递J,只需在签名中使用J而不是I。类型一致性将在编译时确保。

顺便说一句,assertNull不是测试此行为的正确方法。您应该让ClassCastException传播并使用JUnit注释:

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

答案 1 :(得分:-1)

这是因为type erasure。通用constaints仅用于编译时检查(代码通过)。编译代码后,IJ泛型类型将替换为结果代码中的Object

所以正在运行的代码看起来像:

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
    }
}