2012-03-20 5 views
5

내 문제 수있는이 조각으로 표현하는 접속 수 :제네릭 오른쪽 유형으로 변환

public interface TheClass<T> { 
    public void theMethod(T obj); 
} 

public class A { 
    private TheClass<?> instance; 

    public A(TheClass<?> instance) { 
     this.instance = instance; 
    } 

    public void doWork(Object target) { 
     instance.theMethod(target); // Won't compile! 

     // However, I know that the target can be passed to the 
     // method safely because its type matches. 
    } 
} 

내 클래스 A 알 수없는 그 제네릭 유형 TheClass의 인스턴스를 사용을. TheClass 인스턴스는 모든 클래스로 매개 변수화 될 수 있기 때문에 Object으로 전달 된 대상이있는 메서드가 있습니다. 그러나 컴파일러는 정상적인이 같은 대상을 전달할 허용하지 않습니다.

이 문제를 피하려면 어떻게해야합니까?

더러운 솔루션은 ... 잘 작동하지만, 의미 론적으로 잘못 TheClass<? super Object>

내가 전에 사용, 원시 형식으로 만 TheClass을 인스턴스를 선언하는 것이었다 또 다른 해결책을 인스턴스를 선언하는 것입니다,하지만 나쁜 연습, 그래서 내 실수를 바로 잡으려고.

해결

public class A { 
    private TheClass<Object> instance; // type enforced here 

    public A(TheClass<?> instance) { 
     this.instance = (TheClass<Object>) instance; // cast works fine 
    } 

    public void doWork(Object target) { 
     instance.theMethod(target); 
    } 
} 
+2

왜 'A'도 입력하지 않으시겠습니까? –

+0

+1, 좋은 질문입니다! 답변을 기다리는 중 ... – aProgrammer

+0

A는 많은 사람들이 사용하는 오픈 소스 라이브러리의 일부이며 A를 입력하면 강력한 API 중단을 의미합니다. 나는 최근에 "TheClass"인스턴스에 원시 타입을 사용했기 때문에이 문제를 발견했습니다. 왜냐하면 나는 그것이 나쁜 개념이라는 것을 알지 못했기 때문입니다. 그것은 정상적으로 작동했지만 나쁜 습관으로 간주되어 오용을 바로 잡으려고합니다. –

답변

4
public class A { 
    private TheClass<Object> instance; 

    public A(TheClass<Object> instance) { 
     this.instance = instance; 
    } 

    public void do(Object target) { 
     instance.theMethod(target); 
    } 
} 

또는

public class A<T> { 
    private TheClass<T> instance; 

    public A(TheClass<T> instance) { 
     this.instance = instance; 
    } 

    public void do(T target) { 
     instance.theMethod(target); 
    } 
} 
+0

+1 때문에 코드는 이전과 같이 계속 작동합니다. 이것은 내가 입력 한 것입니다. 하지만 먼저 거기에있어! –

+0

당신의 첫 아이디어가 저에게 해결책을 보냈습니다, 고마워요! 사실, 나는 당신 같은 인스턴스 필드의 선언에 Object 타입을 적용합니다. 그러나 생성자 매개 변수에는 여전히 와일드 카드가 허용되지만이 인스턴스는 강제 적용 유형으로 캐스팅됩니다. 이것은 컴파일시에만 효과가 있으며 사용자들에게는 아무 것도 요구하지 않습니다 (질문의 마지막 편집 참조). –

1

용액은 A를 입력한다. 와일드 카드 ?을 사용하면 TheClass의 유형 정보가 손실되므로 나중에 복구 할 방법이 없습니다. 이 당신이 할 수있는 몇 가지 추악한 해킹하지만 당신의 최고의 기회는 A를 입력 : 그것은 중 어떤 API를 중단하지 않습니다

public interface TheClass<T> { 
    public void theMethod(T obj); 
} 

public class A<T> { 
    private TheClass<T> instance; 

    public A(TheClass<T> instance) { 
     this.instance = instance; 
    } 

    public void doIt(T target) { 
     instance.theMethod(target); 
    } 
} 

.

+0

+1, 감사합니다. 라이브러리에 쓸모없는 복잡성을 추가하기 때문에 A를 입력하는 것은 나에게 불가능한 일이지만, 당신이 맞습니다. 그것은 단지 몇 가지 해결책 중 하나였습니다. –

1

컴파일 오류에 대한 이유? 와일드 카드가 자바에서 알 수없는 유형을 나타내는 것입니다. 변수를 알 수없는 일반 매개 변수로 선언 할 수 있지만 을 인스턴스화 할 수 없습니다. 즉, 생성자에서 generic 클래스로 전달 된 것을 나중에 사용하려고 시도하는 것과 호환되지 않는 유형을 유지하기 위해 만들 수 있음을 의미합니다. 현시점의 사례 :

public class A { 
    public static void main(String[] args) { 
     TheClass<String> stringHolder = null; // should constrain parameters to strings 
     A a = new A(stringHolder); 
     a.donot(Float.valueOf(13)) ; // this is an example of what could happen 
    } 

    private TheClass<?> instance; 

    public A(TheClass<?> instance) { 
     this.instance = instance; 
    } 

    public void do(Object target) { 
     instance.theMethod(target); 
    } 
} 

이 경우 컴파일러는 버그가 발생했을 가능성이있는 코드를 작성하지 못하게합니다. 다른 사람들이 지적했듯이 허용되는 유형을 제한하기 위해 A 클래스에 제네릭 매개 변수 유형을 추가해야합니다. 그러면 컴파일 시간 오류가 제거됩니다.

일부 추천 읽기 : Oracle Generics Trail

+0

감사합니다. 나는이 문제를 해결하기 위해 과거의 세부적인 제네릭 이론을 배웠으므로 그 메커니즘을 이해합니다. 원시 타입을 사용하기 전에이 이론을 알고 있다면 나는 그런 상황에 있지 않았을 것입니다. 이제 끝났으므로 원시 형식을 제거하려고했지만이 문제가 발생했습니다. –

+0

어쨌든, 런타임에 코드가 실패하고 예외가 발생하고 프로그램이 중지됩니다. 괜찮습니다. 암시 적 암시 (assertion)와 같으며 오류는 사용자에게 코드에 실수가 있음을 보여줍니다. 그것은 컴파일 시간 오류가 아니지만 여전히 오류가있어 정의되지 않은 동작이 없을 것입니다. 디버그 스택 트레이스를 사용하여 신선하고 깨끗한 충돌이 발생합니다. 예를 들어 +1하면 사용자가 컴파일 - 시간 확인이 중요합니다. –

+0

@ AurélienRibon - 내가 올린 코드는 설명의 목적으로 만 사용되었습니다. 'do' 메소드를'donot'으로 변경했지만, 오류를 수정하는 한 그게 전부입니다. – Perception

관련 문제