2013-10-18 26 views
7

Ben şu sınıfları:Fabrika ve jenerik

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) { 
    ... 
    } 
} 

ben veri türüne bağlı olarak doğru uygulama dönmek bir fabrika kullanmak istiyorum.

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; 
} 

Yanlış mı yapıyorum: Ben şu ama olsun "Kontrol Edilmemiş dökme" uyarıları yazdı? Uyarılardan kurtulmak için ne yapabilirim?

+0

Bu sorunuza cevap vermez ama güvenle yerine 'equals' ait Class'es' için '==' kullanabilirsiniz. – Boann

cevap

3

Jenerikler, derleme zamanı türü güvenliği içindir. Bunun gibi çalışma zamanı türünün belirlenmesi için kullanılamazlar. Bu uyarıdan kurtulmak için, "Raw Types" part of the Java tutorial'da açıklandığı gibi @SuppressWarnings("unchecked") gibi bir şey yapabilir veya -Xlint:-unchecked derleyici bayrağını kullanabilirsiniz.

5

@SuppressWarnings("unchecked") olmadan bu uyarılardan kurtulmanın herhangi bir yolunun farkında değilim.

Class nesnesini iletiyorsanız, T yakalanabilir. Ancak, hangi IDataSource<T> öğesinin döneceğini belirlemek için çalışma zamanında Class'u denetlemeniz gerekiyor. Şu anda, yazım silme işlemi çok zaman geçti.

Java, derleme zamanında, tür güvenliğinden emin olamaz. Çalışma zamanında Class10'daki T'un, IDataSource<T> döndürülenle aynı T olması garanti edilemez, bu nedenle uyarı üretir.

Bu, uyarıyı kaldırmak için @SuppressWarnings("unchecked") yöntemine ek açıklama eklemek zorunda kaldığınız zamanlardan birine benziyor. Bu uyarı bir sebepten dolayı var, bu yüzden tip güvenliği sağlamak ve sağlamak size kalmış. Yazılı olarak, tip güvenliği sağladığınız anlaşılıyor.

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

Doğru yapıyorsunuz ve yalnızca uyarıları bastırmalısınız. Fabrikalar, jenerik türlerde elle yazmak için gerçekten ihtiyacınız olan zorlu alanlardan biridir ve geri dönen değerin, girdiğiniz Class<T> ile eşleştiğinden emin olmanız gerekir. Örneğin, bu durumda IDataSource uygulamalarının bir çift kodlamasını kodlamak için, türlerin doğru olduğunu doğrulayan birim sınamalarını öneririm, böylece MyData uygulaması uyumsuz bir şekilde değişirse, derlemede bir hata alırsınız.

@SuppressWarnings("unchecked") ile getDataSource yöntemine açıklama ekleyin ve uyarıları bastırırken açıklayıcı bir açıklama eklemek her zaman iyi bir fikirdir.

3

Diğer yanıtlar, sorunuzu yanıtlarken bu sorunu yanıtladı. Ama bu fabrika yöntemiyle neyi başarmaya çalıştığınızı anlamak için bir adım geri atmak isterim. Bu fabrika temel olarak IDataSource parametresine bir veri tipi haritası sunar. Dependency injection daha uygun bir model olabilir, çünkü bu iyi bilinen küçük veri türleri ve uygulamaları kümesidir (örneğinizle belirtildiği gibi). IDataSource<Widget> uygulayan bir MongoWidgetDataSource ve IDataSource<Gadget> uygulayan bir MysqlGadgetDataSource:

Diyelim ki MySQL tüm Mongo içinde Widgets ancak tüm Gadgets saklamak istediğinizi varsayalım

, iki sınıfları olabilir. Bir veri yolu kullanıcısı, bir tüketici yöntemi içinde MyFactory.getDataSource(Widget.class) gibi bir fabrika yöntemini kodlamak yerine, uygun IDataSource bağımlılığını enjekte ederim.Biz (Mongo'da saklanan) widget'larla şey yapar MyService olabilir. Eğer önerildiği üzere bir fabrika kullanarak şu şekilde görünecektir:

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

Bunun yerine, hizmete bir yapıcı arg gibi uygun bir veri kaynağını enjekte edilmelidir:

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 
    } 
} 

Bu yapım yararı vardır senin kod daha tekrar kullanılabilir ve daha kolay birim testi (sahte bağımlılıklar). Eğer MyService örneğini Sonra

, ayrıca veri kaynaklarını kadar bağlayabilirsiniz. Pek çok proje bu daha kolay, ama onun değil katı bir gereklilik yapmak için (Guice gibi) bağımlılık enjeksiyon çerçeve kullanır. Şahsen olsa, asla biri olmadan herhangi bir gerçek boyutu veya süresi bir proje üzerinde çalışıyoruz.

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 olarak, böyle bu veri kaynaklarını kadar Tel ediyorum: Eğer bir DI çerçeve kullanmak yoksa arama hizmeti oluştururken

, sadece bağımlılıkları örneğini

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); 
    } 
} 

Bağımlılık inversiyon sorun hakkında düşünmek için farklı bir şekilde biraz, ama çok daha, üretimden bağımsız yeniden kullanılabilir ve sınanabilir kod tabanına yol açabilir.

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); 
} 

ben varmış gibi boş tipimiz ziyade döküm null oluşturmayı tercih edebilir ama bu kalıcı bir tekniktir düşünüyorum: