2016-08-04 10 views
0

왜 MainCain()에서 세 번째 명령문의 주석 처리를 제거 할 때 ClassCastException이 발생합니까? 예외는 없지만 첫 번째와 두 번째 성명서를 잘 작성 했습니까?Java Generics "매개 변수화되지 않은 형식"upcast "

public class Tuple<K, V> { 
    public final K first; 
    public final V second; 

public Tuple(K first, V second) { 
    this.first = first; 
    this.second = second; 
} 

@Override public String toString() { 
    return "Tuple{" + "first = " + first + ", second = " + second + '}'; 
    } 
} 

class Test { static Tuple f(){return new Tuple("test", 8);} } 

class Bar {} 

class Main{ 
    public static void main(String[] args) { 
     Tuple<String, Bar> t = Test.f(); 
     System.out.println(t); 
     //System.out.println(t.second.getClass().getSimpleName()); 
    } 
} 

미리 감사드립니다. Java Docs 당으로

+0

나의 이해가 자바에서 작동하지 않습니다이다. 귀하의 제네릭이 유형을 부여받지 못했습니다, 그들은 제네릭입니다. 이것은 당신이'public final K '을 먼저 가지고 있다면 그것은 작동 할 것이고 그것은 String을 반환 할 것입니다. –

+0

'Test.f()'는 원시 타입을 반환합니다. 이 오류는 원시 형식을 사용하지 않아도 쉽게 포착 할 수 있습니다. 'Test.f()'의 반환 유형은 반환되는 값과 일치하도록'Tuple '로 정의되어야합니다. 그러면'Tuple t = Test.f()'에 컴파일 오류가 발생합니다. 유형 안전은 강력한 기능입니다. –

+0

"스레드의 예외"메인 "java.lang.ClassCastException : java.lang.Integer를 Bar에 캐스트 할 수 없습니다"라는 오류 메시지를 게시해야합니다. 이제는 훨씬 더 명확 해지고 있습니다 ... – alfasin

답변

3

이 방법의 체인 호출

System.out.println(t.second.getClass().getSimpleName()); 

컴파일러 효과적으로이 확장 : t.second는 제네릭 형식임을 발생하는 경우, 지금

TypeOfTSecond tmpTSecond = t.second; 
Class<?> clazzTmp = tmp.getClass(); 
String nameTmp = clazzTmp.getSimpleName(); 
System.out.println(nameTmp); 

, 컴파일러는 삽입합니다 유형에 캐스트하면 t.second이됩니다 :

Bar tmpTSecond = (Bar) t.second; 

Bar 특정 기능에 액세스하지 않으므로 ClassCastException이 표시됩니다.


이것을 설명하기 위해, 여기에서 바이트 코드이다 스택으로 푸시된다 t.second 8은

public static void main(java.lang.String[]); 
    Code: 
     0: invokestatic #2  // Method Test.f:()LTuple; 
     3: astore_1 
     4: getstatic  #3  // Field java/lang/System.out:Ljava/io/PrintStream; 
     7: aload_1 
     8: getfield  #4  // Field Tuple.second:Ljava/lang/Object; 
     11: checkcast  #5  // class Bar 
     14: invokevirtual #6  // Method java/lang/Object.getClass:()Ljava/lang/Class; 
     17: invokevirtual #7  // Method java/lang/Class.getSimpleName:()Ljava/lang/String; 
     20: invokevirtual #8  // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     23: return 

라인; 11 번 행은 Bar에 대한 캐스트가 발생하는 곳입니다.test.f() 선언 할 때


이 약 때문에 사용되는 원료 종류의 제공이가 올바르게 다음

static Tuple<String, Integer> f(){return new Tuple<>("test", 8);} 

이 선으로 선언 된 경우

static Tuple f(){return new Tuple("test", 8);} 

Tuple<String, Bar> t = Test.f(); 

은 컴파일되지 않습니다. 그러나 원시 타입을 사용하면 컴파일러의 타입 검사가 불가능하므로 이와 같은 런타임 오류는 방지 될 수 없습니다.


주요 테이크 아웃 강의는 never use raw types입니다.

2 차 레슨은 컴파일러 (또는 IDE) 경고에주의를 기울입니다.

Note: Main.java uses unchecked or unsafe operations. 
Note: Recompile with -Xlint:unchecked for details. 

및 그 플래그를 사용하여 컴파일 할 때 :이 코드를 컴파일, 나는 들었다

Main.java:19: warning: [unchecked] unchecked call to Tuple(K,V) as a member of the raw type Tuple 
     return new Tuple("test", 8); 
      ^
    where K,V are type-variables: 
    K extends Object declared in class Tuple 
    V extends Object declared in class Tuple 
Main.java:26: warning: [unchecked] unchecked conversion 
    Tuple<String, Bar> t = Test.f(); 
           ^
    required: Tuple<String,Bar> 
    found: Tuple 
2 warnings 
+0

Thnks, 완벽하게 명확한 답변입니다! –

+0

이 경우 컴파일러는'bar'에't.second'의 형 변환을 삽입 할 필요가 없습니다. 이는 구현에 따라 다릅니다. – newacct

0

, getClass() 방법 클래스 Object

실제 결과 유형이 Class<? extends |X|> 때문이다 | X |은 getClass가 호출 된 표현식의 정적 유형을 지우는 것입니다. IntegerBar 사이에 부모 - 자식 관계가 바의 정수를 캐스팅하는 동안, 당신은 ClassCastException를 얻을 수 없기 때문에 선언 부분에서

이 유형 바

Tuple<String, Bar> t = Test1.f(); 

이다 (즉 Integer extends Bar은 아니다) 여기서 올바른

는 수정을

Tuple<String, Object> t = Test1.f(); 

주이며 :It is not encouraged to use raw types. 이 대답은 실패의 원인과 그에 대한 해결책을 설명합니다. 당신이 쓸 때

+0

아래쪽 유권자가 문제가 무엇인지 명확히하지 않으면 – JavaHopper

+0

다운 vote는 사용하지 않습니다. downvote하지 않았지만 원시 유형 사용에 기반하여 수정 되었기 때문에 추측하고 있습니다. 'Test.f()'가 반환되는 값 ('')과 일치하는 타입 인자로 선언 되었다면' '를 사용하여 컴파일 에러가 발생합니다. 작동하지만 경고 메시지를 통해 표시되는 실제 문제 (유형 안전성 부족)를 피할 수 있으므로 잘못된 디자인을 권장합니다. 나는 개인적으로 이런 이유로 downvote하지 않을 것이지만 (여전히 작동 함), 어떤 사람들은 다른 사람들보다 엄격하다. –

+0

나는 그것을 얻는다! 따라서 수정을 제공하고 수정 이유를 설명하기보다는 모범 사례를 제안해야합니다. 당신이 말했듯이, 나는 원시 타입의 사용이 권장되지 않는다고 말하는 것을 놓쳤습니다. 감사! – JavaHopper