2015-10-07 4 views
0

У меня проблема в java, которую я не понимаю, может кто-нибудь объяснить мне это странное поведение?Java Generic Cast Error

мой код:

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: [email protected] 
    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 из данных, у меня есть 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) 

ответ

-1

Это из-за type erasure. Общие константы предназначены только для проверки времени компиляции (который проходит ваш код). После того как ваш код будет скомпилирован, в полученном коде будут заменены I и J типовыми типами.

Так работает код действительно выглядит следующим образом:

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

Спасибо, я не знаю, что я буду окружать этот бросок с тестом с использованием InstanceOf вместо попытки поймать –

+0

@ aurélienlemaitre Нет ... просто измените подпись на 'setData (J data)'. Вы убиваете точку дженериков здесь – Dici

+0

Упс, я не могу использовать 'instanceof', но я все еще нуждаюсь в сигнатуре, которую я использовал ... Как я могу обрабатывать плохие кавычки без' instanceof' или 'try catch'? –

3

Man вы подавили предупреждение о компиляции, и вы спрашиваете, почему ваш код имеет странное поведение во время выполнения? Ваш листинг в setData не установлен из-за стирания типа, это означает, что он будет никогда не работает. Если вы хотите передать J, просто используйте J в подписке вместо I. Консистенция типа будет обеспечивать время компиляции.

К сожалению, assertNull не был правильным способом для изучения этой ситуации. Вы должны позволить ClassCastException распространяться и использовать JUnit аннотаций:

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