2010-11-22 3 views
7

누군가 두 번째 클래스가 컴파일되지 않는 이유를 설명 할 수 있습니까?반환 유형이 지우기의 일부입니까?

1

public class SameSignatureMethods { 
    public <T extends String> Boolean test() 
    { 
     return true; 
    } 

    public <T extends Character> Double test() 
    { 
     return 1d; 
    } 
} 

이 그 예에 작은 변화 (이클립스 코드를 불평) javac의 및 JDK 6을 이용하여 미세 컴파일하고 컴파일 다음 오류 실패

name clash: <T>test() and <T>test() have the same erasure 

메서드의 반환 유형 만 변경 :

public class SameSignatureMethods { 
    public <T extends String> Boolean test() 
    { 
     return true; 
    } 

    public <T extends Character> Boolean test() { 
     return true; 
    } 
} 

그 방법은 전나무 t 클래스 보일 것이다

public static void main(String[] args) { 
    SameSignatureMethods m = new SameSignatureMethods(); 
    System.out.println("m.<Character>test()=" + m.<Character>test()); 
    System.out.println("m.<String>test()=" + m.<String>test()); 
} 
+0

흠, 흥미로운 것. 어떤 컴파일러를 사용 했습니까? JDK 또는 일부 IDE 내장? 첫 번째 것은 Eclipse 3.6 SR1에서 컴파일되지 않습니다 (올바른 것입니다). – BalusC

+0

java 버전 "1.6.0_18" Java (TM) SE 런타임 환경 (빌드 1.6.0_18-b07) Java HotSpot (TM) 클라이언트 VM (빌드 16.0-b13, 혼합 모드) – Yuriy

+0

@BalusC 반환 유형이 아닙니다. 메서드 서명의 일부입니까? 오류 메시지는 이상합니다. – extraneon

답변

4

그래서 JDK 컴파일러는 첫 번째 버전을 컴파일하지만 두 번째 버전은 컴파일하지 않지만 Eclipse 컴파일러는 두 버전을 컴파일하지 않습니다.

Java 바이트 코드의 관점에서, 첫 번째 버전은 형식이 완전히 삭제 된 두 가지 방법, 즉 public java.lang.Boolean test()public java.lang.Double test()을 포함하고 있으며, 이는 완전히 유효합니다. 제네릭 메서드를 재정의 할 때 JDK 컴파일러와 Eclipse 컴파일러에서 이러한 메서드를 생성하는 경우가 있지만 이러한 메서드는 합성 브리지 메서드로 표시됩니다.

두 번째 버전에는 동일한 서명 (삭제 후)이있는 두 개의 메소드가 포함되어 있으며 Java 바이트 코드에서는 사용할 수 없습니다.따라서 JDK 컴파일러는 이러한 클래스 파일을 생성 할 수 없습니다. 난 그냥 같은 방법으로 클래스를 생성하는 16 진수 편집기와 클래스 파일을 편집하고, 프로그램을 시작하기에,이 오류가 발생합니다 :

Exception in thread "main" java.lang.ClassFormatError: Duplicate method name&signature in class file SameSignatureMethods 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClassCond(Unknown Source) 
    at java.lang.ClassLoader.defineClass(Unknown Source) 
    at java.security.SecureClassLoader.defineClass(Unknown Source) 
    at java.net.URLClassLoader.defineClass(Unknown Source) 
    at java.net.URLClassLoader.access$000(Unknown Source) 
    at java.net.URLClassLoader$1.run(Unknown Source) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(Unknown Source) 
    at java.lang.ClassLoader.loadClass(Unknown Source) 
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source) 
    at java.lang.ClassLoader.loadClass(Unknown Source) 
Could not find the main class: SameSignatureMethods. Program will exit. 

내가 이런 모습으로 시작하는 클래스입니다. 그들은 같은 이름의 길이가 있기 때문에 내가 문자열과 더블을 사용 : 다음

public class SameSignatureMethods { 
    public <T extends String> String test() { 
     return null; 
    } 

    public <T extends Double> Double test() { 
     return null; 
    } 

    public static void main(String[] args) { 
     System.out.println(new SameSignatureMethods().<Double>test()); 
    } 
} 

를 16 진수 편집기를 사용하여, 나는 클래스 파일의 두 장소, 원시 하나에, public <T extends String> Double test()에 첫 번째 방법의 서명을 변경 서명 ()Ljava/lang/Double;, 일반 서명 <T:Ljava/lang/String;>()Ljava/lang/Double;을 포함하는 서명

+0

조금 더 자세히 설명해 주시겠습니까?반환 유형이 서명의 일부가 아니라고 생각했습니다. – Yuriy

+1

반환 값을 무시하는 호출에 대해 컴파일러에서 선택할 수 없으므로 반환 유형은 클래스를 컴파일 할 때 서명의 일부가 아닙니다. 그러나 바이트 코드에서 반환 유형은 서명의 일부입니다. 두 클래스를 컴파일하여 다른 클래스의 메소드를 호출 한 다음 호출 된 메소드의 리턴 유형 만 변경하여이를 테스트 할 수 있습니다. 해당 클래스를 컴파일하고 호출 클래스는 컴파일하지 말고 프로그램을 실행하십시오. NoSuchMethodError가 발생합니다. –

+0

JVM을 바이트 코드 검증기를 사용하지 않고 시작한 경우 (권장하지 않음!) JVM이 동일한 서명으로 두 메소드 중 하나를 호출하기 때문에 프로그램이 실행되고'null '을 인쇄합니다. 두 메소드의 서명을 변경하여'String'을 대신 리턴하면 프로그램은 NoSuchMethodError로 실패합니다. 두 메소드 중 어느 것도 main 메소드가 요청한 반환 유형'Double '을 가지고 있지 않기 때문입니다. –

1

를 일류 (SameSignatureMethods)에서 각각 실행 창 BooleanDouble에서, 2 가지 방법. 두 번째 클래스에서 메서드는 모두 Boolean을 반환합니다.

<T extends String>이 메서드 정의 앞에 있다고해서 그것이 반환 형식이라는 것을 의미하지는 않습니다.

아마 당신은이 같은 싶지 :

public <T extends String> T test() 
{ 
    return obj;// obj could be a String 
} 

그러나, 일반적인 유형은 런타임에 지워지고 방법은 위의 당신이 혼란스럽게 관리해야처럼

public Object test() 
{ 
    return obj;// obj could be a String 
} 
+1

예,하지만 왜 첫 번째 작업을 했습니까? 하나의 컴파일? – BalusC

+0

두 클래스의 메소드는 실제 타입을 반환합니다. 선언 된 동안 제네릭은 전혀 사용되지 않습니다. 두 번째 메소드가 동일한 이름과 인수뿐 아니라 동일한 리턴 유형을 갖는 클래스 간의 유일한 차이점 - 그리고 컴파일러가 오류를 던지게하는 이유 Generics를 제거하면 둘 다 컴파일되지 않지만 처음에는 실제로 컴파일됩니다. – Yuriy

+0

첫 번째 클래스가 컴파일되지 않습니다 :'SameSignatureMethods 형식의 중복 메소드 테스트(). 코드를 올바르게 복사하지 않습니다. 이미 내 답변에서 말했듯이 : 제네릭 형식 정의 _는 반환 형식임을 의미하지 않습니다. _ 나중에 메서드의 내용이나 para에서 사용할 수 있습니다 미터이지만, 리턴 타입은 여전히 ​​당신의 경우'Boolean'이다. –

2

는 소리가 될 것입니다 당신의 끔찍하게 컴파일러 :

  • 반환 유형은 서명의 일부가 아닙니다. 컴파일러는 return 형식을 사용하여 호출되는 메서드를 알 수 없습니다.

  • 예제에서 메서드 서명에 포함 된 일반 내용은 반환 형식에 아무런 영향을주지 않습니다.

  • 또한 <T extends String>은 의미가 없습니다. 최종 유형을 확장 할 수 없습니다. (흠, 이것은 단지 경고 일뿐입니다. 컴파일을 멈추지 않습니다)

두 번째 클래스가 컴파일되지 않는 이유가 궁금합니다. 첫 번째 클래스가 컴파일되는 이유가 궁금합니다. 마찬가지로 첫 번째 클래스는 경고와 함께 컴파일됩니다. 뾰족한 브래킷 물건을 꺼내면 '중복 메소드'오류가 발생하며 그 오류는 무시하지 않아도됩니다. 컴파일러 버그 여야합니다.

+0

글쎄, 실제로는 이론적 인 문제입니다. 1.6.0_18 컴파일러를 얻는 것만 큼 표준이며 다시 컴파일해야합니다. 즉, 퍼스트 클래스가 컴파일 할 때 완벽한 의미를 갖습니다. Java 스펙과 일치합니다. 컴파일하지 않는 두 번째 - 컴파일러 버그로 인해 – Yuriy

+1

예 # 1이 컴파일됩니다. - http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6182950- 더 이상 컴파일하지 않습니다. JDK7 (또는 이클립스 3.6+도 수정했습니다) –

관련 문제