Java : Cyclic Generic Type 관계는 SuperType (Javac Bug)에서 캐스트를 허용하지 않습니다.

StackOverflow https://stackoverflow.com/questions/804247

  •  03-07-2019
  •  | 
  •  

문제

나는 Java 컴파일러의 완전히 이상한 동작을 겪습니다.
나는 슈퍼 타입을 하위 유형에 시전 할 수 없다 주기적 제네릭 유형 관계 관련되어 있습니다.

주니트 테스트 케이스 문제를 재현하려면 :

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

참고 : NetBeans 최신 트렁크, 번들 개미, 최신 Java 6 릴리스를 사용하고 있습니다.
명령 줄에서 Ant를 사용하려고 시도했지만 (NetBeans는 build.xml 파일을 생성합니다) 동일한 오류가 발생합니다.

뭐가 잘못 되었 니?
우아한 방법이 문제를 해결합니까?

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

편집하다:
아니, 이제 이해합니다 아무것도 아님!
Eclipse 3.4.1은 경고 나 오류를 표시하지 않으며 문제없이 코드를 컴파일합니다 !!!
어떻게 이럴 수있어? NetBeans가 제공하는 build.xml과 함께 명령 라인의 개미를 사용하는 것은 중립적 일 것이라고 생각했습니다.
내가 뭔가를 놓치고 있습니까?

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

편집 3 :
Javac Bug를 다루고 있음을 나타 내기 위해 제목이 변경되었습니다.

도움이 되었습니까?

해결책

복잡한 일반 유형을 쉽게 이해한다고 주장하지는 않지만 컴파일하는 코드를 찾으면 javac 그리고 안됩니다 ecj (Eclipse Compiler), 두 사람 모두 버그 보고서를 제출하십시오. 그리고 그리고 상황을 명확하게 설명하십시오 (버그 보고서를 모두 제출하고 해당 URL을 언급했다고 언급 한 경우 가장 좋습니다. 태양의 경우 버그가 공개적으로 액세스하기 전에 시간이 걸릴 수 있습니다).

나는 과거에 그것을했고 정말 좋은 반응을 얻었습니다.

  1. 팀 중 하나는 올바른 접근 방식이 무엇인지 알아 냈습니다 (컴파일 오류, 경고 또는 아무것도 없음)
  2. 결함이있는 컴파일러가 고정되었습니다

두 컴파일러 모두 동일한 사양을 구현하므로 그 중 하나만 정의상 잘못된 것입니다. 그 중 하나만 코드를 컴파일하면.

기록을 위해 :

샘플 코드를 컴파일하려고했습니다 javac (Javac 1.6.0_13) 및 ecj (Eclipse Java 컴파일러 0.894_R34X, 3.4.2 릴리스) 및 javac 큰 소리로 불만을 제기하고 생산하지 못했습니다 .class 파일 ecj 미사용 변수 (경고)에 대해서만 불만을 제기하고 예상되는 모든 것을 생성했습니다. .class 파일.

다른 팁

나는 이것을 위해 비 게이너리를 사용하게되었습니다.

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

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

와일드 카드가없는 유형을 사용하지 않도록하는 것은 무엇입니까?

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
}

그렇게하면 훨씬 더 명확하게 읽히고 컴파일하고 실행됩니다. :) 나는 이것이 "우아한 방법이 문제를 해결할 수있을 것입니다"라고 생각합니다.

문제는 캐스트하려는 것입니다 IAtom<?, ?> 에게 Atom (이것은 Atom<Atom, Space>). 도대체가 어떻게 그것을 알아야 하는가 ? 원자와 공간 일 수 있습니까?

제네릭이 채워진 곳에 고착 할 유형을 모르면 일반적으로

ISpace spaceSupertype = new Space();

이로 인해 컴파일러 경고가 발생하지만 (오류가 아님) 코드가 여전히 실행됩니다 (실제 유형이 호환되지 않으면 런타임 오류가 발생합니다).

그러나이 모든 것은 보는 것이 합리적이지 않습니다. 당신은 당신이 강한 타이핑이 필요하다고 말하면, 당신은 ? 유형이가는 곳. 그런 다음 돌아 서서 캐스팅하려고합니다.

우주와 원자에 캐스트 할 수 있어야한다면 아마도 시작하기 만하면됩니다. 결국 해당 변수에서 다른 유형을 고수 할 수 없다면 결국에는 런타임 유형을 변경할 때 코드가 모든 도대체처럼 끊어 질 것입니다. 이 의견의 끝).

그러나 실제로, 당신 이이 이상한 일을하고 있다면, 우리는 코드 디자인이 좋지 않은 것을보고 있다고 생각합니다. 당신이 이것을 어떻게 구성하고 있는지 다시 생각하십시오. 여기에있는 기능을 수행하려면 다른 클래스 나 인터페이스가 필요할 수 있습니다.

"정말 강한 유형이 필요합니까? 무엇을 얻습니까?" 인터페이스를 사용하고 있기 때문에 필드를 추가하는 것이 좋습니다. (필드가 메소드를 통해서만 액세스하는 경우 공개 인터페이스에 메소드를 추가하는 것만으로도 메소드를 추가하면 단일 인터페이스를 사용합니다. IAtom 그리고 ISpace 여기에만 사용할 수 있기 때문에 나쁜 생각이 있습니다. test() 어쨌든 그 하위 유형으로. test() 다른 구현으로 일반화되지 않습니다 ISpace/IAtom. 여기에 넣을 모든 구현에 필요한 방법과 동일한 방법이 있습니다. test() 그러나 모든 구현은 아닙니다 IAtom/ISpace 중간 하위 인터페이스가 필요합니다.

public interface IAtom2 extends IAtom
{
    [additional methods]
}

그런 다음 사용할 수 있습니다 IAtom2 대신에 IAtom 안에 test(). 그런 다음 필요한 타이핑을 자동으로 얻었으며 제네릭이 필요하지 않습니다. 클래스 세트에 모두 공통 공개 인터페이스 (메소드 및 필드 세트)가있는 경우 해당 공개 인터페이스는 슈퍼 형 또는 인터페이스를위한 좋은 후보입니다. 내가 묘사 한 것은 평행 사변형, 사각형, 사각형 관계와 같은 사각형 부분을 건너 뛰려 고합니다.

재 설계하지 않으려면 다른 아이디어는 순환 제네릭을 떨어 뜨리고 인스턴스 테스트를 통해 인스턴스 테스트를 수행한다는 것입니다. instanceof:

if (spaceSupertype instanceof Space)
{
    Space space = (Space)spaceSupertype;
    ...
}
else
...
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top