2009-04-29 1 views
5

Java 컴파일러의 동작이 완전히 이상합니다.
주기 일반 유형 관계과 관련된 경우 수퍼 유형을 하위 유형으로 캐스트 할 수 없습니다. 개미, 최신 자바 6 번들 내가 사용 넷빈즈 최신 트렁크 :자바 : 순환 제네릭 형식 관계가 상위 형식에서 캐스트를 허용하지 않습니다 (javac 버그)

public class _SupertypeGenericTest { 

    interface ISpace<S extends ISpace<S, A>, A extends IAtom<S, A>> { 
    } 

    interface IAtom<S extends ISpace<S, A>, A extends IAtom<S, A>> { 
    } 

    static class Space 
      implements ISpace<Space, Atom> { 
    } 

    static class Atom 
      implements IAtom<Space, Atom> { 
    } 

    public void test() { 
     ISpace<?, ?> spaceSupertype = new Space(); 
     IAtom<?, ?> atomSupertype = new Atom(); 

     Space space = (Space) spaceSupertype; // cast error 
     Atom atom = (Atom) atomSupertype; // cast error 
    } 
} 

컴파일러 에러 출력 :

_SupertypeGenericTest.java:33: inconvertible types 
found : pinetag.data._SupertypeGenericTest.ISpace<capture#341 of ?,capture#820 of ?> 
required: pinetag.data._SupertypeGenericTest.Space 
       Space space = (Space) spaceSupertype; 
            ^

_SupertypeGenericTest.java:34: inconvertible types 
found : pinetag.data._SupertypeGenericTest.IAtom<capture#94 of ?,capture#48 of ?> 
required: pinetag.data._SupertypeGenericTest.Atom 
       Atom atom = (Atom) atomSupertype; 
           ^
2 errors 

JUnit 테스트 케이스 문제를 재현하는 방법 해제.
명령 줄에서 Ant를 사용하여 시도했습니다 (Netbeans가 build.xml 파일을 생성 함) 하지만 오류가 발생합니다.

무엇이 잘못 되었나요?
문제를 해결하는 우아한 방법이 있습니까?

이상한 점은 : Netbeans은 주어진 코드에서 오류 (심지어 경고는 표시하지 않음)를 표시하지 않습니다.

편집 :
아니, 지금은 아무것도을 이해하지!
Eclipse 3.4.1은 경고 나 오류를 표시하지 않으며, 문제없이 코드를 에 컴파일합니다!
어떻게 될 수 있습니까? 내 생각에, Netbeans에서 제공 한 build.xml과 함께 커맨드 라인에서 Ant를 사용하면 중립적이라고 생각했습니다.
뭔가 부족합니까?

편집 2 : JDK7 라이브러리와 JDK7 코드 형식을 사용
는 NetBeans는 오류/경고없이 컴파일!
(I은 1.7.0-EA-B55를 사용하고 있습니다)

편집 3 :
우리는 javac의 버그 상대하고 있음을 나타 내기 위해 제목을 변경.

+0

어 - 오. 전에 ISpace 및 IAtom에서이 물건을 보지 못했습니까? 그리고 나는 그 중 하나를 이해할 수 없었다. –

+0

좋습니다. 나는 * 그것을 * 전에 보았고, 그 이후로 더 복잡해졌습니다. –

+0

그래, 그거야;) 순환 형 제네릭 형식 관계의 목적은 다음과 같습니다. '스페이스'가 강하게 입력 된 'Atoms'를 반환하고 'Atom'이 강력한 형식의 부모 ' 공간'. 또한, 많은 하위 유형 envolved ... 긴 이야기;) –

답변

2

당신이 javac에서 컴파일하지 않는 몇 가지 코드를 찾을 수 있다면 쉽게 그 복잡한 일반적인 유형을 이해하는 주장하지만,하지 않습니다 ecj (이클립스 컴파일러)에있는 경우 SunEclipse과 함께 버그 보고서를 제출하고 두 가지 버그 보고서를 모두 작성하고 각각의 URL을 언급했다고 언급 한 경우 가장 좋은 상황을 설명합니다 (Sun의 경우 다소 시간이 걸릴 수 있음). 버그는 공개적으로 접근 가능하다).

나는 팀의

  1. 하나가 올바른 접근 방식은
  2. 하고 결함 (오류, 경고 또는 아무것도를 컴파일 줄)이 있었는지 알아 낸 곳 과거에 정말 좋은 반응을 가지고 있다고 했어 컴파일러가 수정되었습니다

두 컴파일러 모두 동일한 스펙을 구현하므로 하나만 코드를 컴파일하면 정의 중 하나가 잘못됩니다. 기록을 위해

: 나는 javac (javac의 1.6.0_13) 및 ecj (이클립스 자바 컴파일러 0.894_R34x, 3.4.2 버전)와 javac 큰 소리로 불평과 샘플 코드를 컴파일하려고

은을 생산하는 데 실패 .class 파일 인 반면 ecj은 사용되지 않은 변수 (경고)에 대해서만 불만을 제기하고 모든 예상 된 .class 파일을 생성했습니다.

0

나는이 비 제네릭을 사용하여 종료했습니다

@Test 
    public void test() { 
      ISpace spaceSupertype = new Space(); 
      IAtom atomSupertype = new Atom(); 

      Space space = (Space) spaceSupertype; // ok 
      Atom atom = (Atom) atomSupertype; // ok 
    } 
0

와일드 카드가없는 유형을 사용하지 못하게하는 요인은 무엇입니까?

public void test() { 
    ISpace<Space, Atom> spaceSupertype = new Space(); 
    IAtom<Space, Atom> atomSupertype = new Atom(); 

    Space space = (Space) spaceSupertype; // no error 
    Atom atom = (Atom) atomSupertype; // no error 
} 

훨씬 명확하게 읽는 방법, 플러스 컴파일 및 실행 : 난이

+0

아니요, 그냥 유스 케이스입니다. 가능한 모든 하위 유형과 작업하려면 수퍼 유형이 필요합니다. –

0

문제는 당신이 캐스팅하려는 것을 수 있습니다 "우아한 방법은 문제를 해결"할 것이라고 생각 IAtom<?, ?> ~ Atom (이는 Atom<Atom, Space> 임). 도대체 시스템이 ?이 원자와 공간 일 수 있다는 것을 알고 있어야합니까? 제네릭가 작성되는 경우 당신이 붙어 종류를 모르는 경우

, 당신은 일반적으로 단지 (하지 오류) 경고 컴파일러를 생성

ISpace spaceSupertype = new Space(); 

같이 전체 일을두고 있지만, 코드가 여전히 실행됩니다 (실제 유형이 호환 가능하지 않으면 런타임 오류가 발생합니다).

그러나이 모든 것이보기에는 의미가 없습니다. 당신은 강한 타이핑이 필요하다고 말하면 유형이있는 곳인 ?을 붙입니다. 그런 다음 뒤돌아서 던지려고합니다.

Space 및 Atom으로 캐스팅 할 수 있어야하는 경우에는 처음부터 사용하도록 설정해야합니다. 이러한 변수에 다른 유형을 고정시킬 수 없기 때문에 결국에는 런타임 유형을 변경할 때 코드가 끊어지기 쉽습니다. if/then 문 (예 : 이 코멘트의 끝).

정말 이상한 일을하는 사람은 코드 디자인이 좋지 않다고 생각합니다. 어떻게 구조화하는지 다시 생각해보십시오. 어쩌면 여기에있는 기능을 수행하기 위해 다른 클래스 나 인터페이스가 필요할 수도 있습니다.

자신에게 물어보십시오. "나는 정말로 강한 유형이 필요한가요? 인터페이스를 사용하고 있기 때문에 필드를 추가하지 않는 것이 좋습니다. (필드가 메서드를 통해서만 액세스되는 경우 공용 메서드에 메서드를 추가하는 것입니다. 메서드가 추가되면 IAtomISpace이라는 단일 인터페이스를 사용하는 것은 나쁜 생각입니다. 어쨌든 test()을 해당 하위 유형과 함께 사용할 수 있습니다. test()ISpace/IAtom의 다른 구현으로 일반화되지 않습니다.

public interface IAtom2 extends IAtom 
{ 
    [additional methods] 
} 

이 그럼 당신은 IAtom2를 사용하는 대신 수 있습니다 : 당신이 퍼팅됩니다 모든 구현하면 여기에 같은 당신이 test()에 필요한 방법하지만 IAtom/ISpace의 모든 구현이 그들을 가지고, 당신은 중간 서브 인터페이스 필요가 IAtom in test(). 그런 다음 자동으로 필요한 입력 내용을 얻었으므로 제네릭이 필요하지 않습니다. 한 세트의 클래스가 모두 공용 공용 인터페이스 (메소드 및 필드 세트)를 가지고있는 경우, 공용 인터페이스는 수퍼 유형 또는 인터페이스가 가질 수있는 좋은 후보입니다. 제가 설명하는 것은 평행 사변형, 직사각형, 정사각형 관계와 같은 것입니다. 여기서 직사각형 부분을 건너 뛸 수 있습니다.

당신이 재 설계하지 않을 경우, 다른 아이디어는 당신이 순환 제네릭을 삭제하고 단지 instanceof를 통해 테스트 인스턴스를 할 것입니다 :

if (spaceSupertype instanceof Space) 
{ 
    Space space = (Space)spaceSupertype; 
    ... 
} 
else 
...