2013-10-18 1 views
7

내가 가지고있는 다음과 같은 클래스 :공장 및 제네릭

public interface IDataSource<T> { 
    public List<T> getData(int numberOfEntries); 
} 

public class MyDataSource implements IDataSource<MyData> { 
    public List<MyData> getData(int numberOfEntries) { 
    ... 
    } 
} 

public class MyOtherDataSource implements IDataSource<MyOtherData> { 
    public List<MyOtherData> getData(int numberOfEntries) { 
    ... 
    } 
} 

내가 데이터 유형에 따라 올바른 구현을 돌려 공장을 사용하고 싶습니다. 나는 다음을 썼지 만 "검사되지 않은 캐스트"경고를 받는다 :

public static <T> IDataSource<T> getDataSource(Class<T> dataType) { 
    if (dataType.equals(MyData.class)) { 
     return (IDataSource<T>) new MyDataSource(); 
    } else if (dataType.equals(MyOtherData.class)) { 
     return (IDataSource<T>) new MyOtherDataSource(); 
    } 

    return null; 
} 

내가 잘못하고 있니? 경고를 없애기 위해 무엇을 할 수 있습니까?

+0

이 질문에 대한 답은 없지만'equals' 대신'Class'es에'=='를 사용할 수 있습니다. – Boann

답변

3

일반 사항은 컴파일 타임 안전을위한 것입니다. 이러한 런타임 유형 결정을 위해 사용할 수 없습니다. 경고를 없애기 위해 과 같은 것을하거나 "Raw Types" part of the Java tutorial에 설명 된대로 -Xlint:-unchecked 컴파일러 플래그를 사용할 수 있습니다.

5

@SuppressWarnings("unchecked")없이 이러한 경고를 제거 할 수있는 방법을 모르고 있습니다.

Class 개체를 전달하므로 T을 캡처 할 수 있습니다. 그러나 런타임에 Class을 확인하여 반환 할 IDataSource<T>을 확인해야합니다. 이 시점에서 유형 삭제가 오래 발생했습니다.

컴파일 타임에 Java는 유형 안전성을 확신 할 수 없습니다. 런타임시 Class에있는 T이 에있는 T과 같을 것이라고 보증 할 수 없으므로 경고를 생성합니다.

경고를 제거하기 위해 @SuppressWarnings("unchecked")과 함께 메서드에 주석을 추가해야하는 경우가 있습니다. 이 경고는 이유가 있기 때문에 유형 안전을 제공하고 보장하는 것은 사용자의 몫입니다. 서면으로, 당신이 타입 안전을 제공 한 것처럼 보입니다.

@SuppressWarnings("unchecked") 
public static <T> IDataSource<T> getDataSource(Class<T> dataType) { 
4

당신은 올바르게하고 있습니다. 경고를 단순히 억제해야합니다. 팩토리는 제네릭 유형의 까다로운 영역 중 하나입니다. 수동으로 generic 형식으로 캐스팅해야하는 경우가 있으며, 반환 된 값이 Class<T>과 일치하는지 확인해야합니다. 예를 들어,이 경우 두 개의 IDataSource 구현을 하드 코딩 했으므로 형식이 맞는지 확인하는 단위 테스트를 작성하여 호환되지 않는 방식으로 MyData 구현이 변경되면 빌드시 오류가 발생하도록하는 것이 좋습니다.

getDataSource 메서드에 @SuppressWarnings("unchecked")이라는 주석을 달기 만하면 경고를 표시 할 때 설명 주석을 추가하는 것이 좋습니다.

3

다른 답변으로 인해 문제가 해결되었습니다. 그러나 나는이 공장 방법으로 무엇을 성취하려고 하는지를 이해하기 위해 한 발 뒤로 물러서 고 싶다. 이 팩토리는 기본적으로 IDataSource 매개 변수에 데이터 유형의 맵을 제공합니다. Dependency injection은 더 잘 알려진 패턴 일 수 있습니다.이 패턴은 잘 알려진 작은 데이터 유형 및 구현 집합이므로 (예제에서와 같이) IDataSource<Widget>를 구현하는 MongoWidgetDataSourceIDataSource<Gadget>를 구현하는 MysqlGadgetDataSource :

의 당신이 MySQL의 모든 몽고에서 Widgets하지만 모든 Gadgets를 저장할 가정 해 봅시다

, 당신은 두 개의 클래스가있을 수 있습니다.

데이터 소비자 내부에서 MyFactory.getDataSource(Widget.class)과 같은 팩토리 메서드 호출을 하드 코딩하는 대신 적절한 IDataSource 종속성을 주입 할 것입니다.MyService에 위젯이있는 항목 (mongo에 저장되어 있음)이있을 수 있습니다. 당신이 제안 된 공장을 사용하면 다음과 같을 것이다 :

public class MyService { 
    public void doSomething() { 
    String value = MyFactory.getDataSource(Widget.class).getSomething(); 
    // do something with data returned from the source 
    } 
} 

대신 서비스로 생성자의 인자로 해당 데이터 소스를 삽입해야합니다

public class MyService { 
    private final IDataSource<Widget> widgetDataSource; 

    public MyService(IDataSource<Widget> widgetDataSource) { 
    this.widgetDataSource = widgetDataSource; 
    } 

    public void doSomething() { 
    String value = widgetDataSource.getSomething(); 
    // now do something with data returned from the source 
    } 
} 

이 만들기의 추가 혜택을 가지고 당신의 코드를 재사용 가능하고 단위 테스트 (모의 의존성)가 더 쉽다.

그런 다음 MyService을 인스턴스화하면 데이터 원본을 연결할 수도 있습니다. 많은 프로젝트에서 종속성 주입 프레임 워크 (예 : Guice)를 사용하면이 작업을보다 쉽게 ​​수행 할 수 있지만 엄격한 요구 사항은 아닙니다. 개인적으로, 나는 실제 크기 나 기간이없는 프로젝트에서 결코 일하지 않습니다.

public static void main(String[] args) { 
    IDataSource<Widget> widgetDataSource = new MongoWidgetDataSource(); 
    IDataSource<Gadget> gadgetDataSource = new MysqlGadgetDataSource(); 
    MyService service = new MyService(widgetDataSource, gadgetDataSource); 
    service.doSomething(); 
} 

Guice에서이 같은 이러한 데이터 소스를 연결할 것입니다 : 당신이 DI 프레임 워크를 사용하지 않는 경우에는 전화 서비스를 만들 때

, 당신은 단지 종속성을 인스턴스화

public class DataSourceModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    bind(new TypeLiteral<IDataSource<Widget>>() {}).to(MongoWidgetDataSource.class); 
    bind(new TypeLiteral<IDataSource<Gadget>>() {}).to(MysqlGadgetDataSource.class); 
    } 
} 

종속성 반전은 문제에 대해 생각하는 약간 다른 방법이지만 훨씬 더 분리되고 재사용 가능하며 테스트 가능한 코드 기반으로 이어질 수 있습니다.

public static <T> IDataSource<T> getDataSource(MyData dataType) { 
    System.out.println("Make MyDataSource"); 
    return (IDataSource<T>) new MyDataSource(); 
} 

public static <T> IDataSource<T> getDataSource(MyOtherData dataType) { 
    System.out.println("Make MyOtherDataSource"); 
    return (IDataSource<T>) new MyOtherDataSource(); 
} 

public void test() { 
    IDataSource<MyData> myDataSource = getDataSource((MyData) null); 
    IDataSource<MyOtherData> myOtherDataSource = getDataSource((MyOtherData) null); 
} 

내가 가지고있는 것처럼 당신은 빈 원형보다는 캐스트 null를 작성하는 것이 좋습니다하지만 난이 가능한 기술이다 생각 :