2016-02-12 613 views
1

無法在Java中工作我在Java編譯器中遇到了一個奇怪的問題。下面是代碼:在Java中使用類<T>和<T extends A>

private <T extends IdentifiedBusinessTransversalEntity> T 
    getOrCreateTransversalEntity(Class<T> classT, String id) { 
...} 

private <T extends IdentifiedBusinessDSEntity> T 
    getOrCreateDSEntity(Class<T> classT, String id) { 
...} 

public abstract class IdentifiedBusinessDSEntity extends 
     BusinessDSEntity implements IdentifiedEntity { 
...} 

public abstract class IdentifiedBusinessTransversalEntity extends 
       BusinessTransversalEntity implements IdentifiedEntity { 
...} 

public <T extends IdentifiedEntity> T getOrCreate(Class<T> classT, String id) 
{ 
    if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT)) 
    { 
     return getOrCreateDSEntity(classT.asSubclass(IdentifiedBusinessDSEntity.class),id); 
    } else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT)) 
    { //must cast explicitly to T here but works well just above and is exactly the same. Strange 
     return (T) getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class),id); 
    } 
    return null; 
} 

我不明白爲什麼在getOrCreate函數,編譯器允許第一回不(T)但不是在第二返回。錯誤是:

類型不匹配:不能轉換從IdentifiedBusinessTransversalEntityT

應該差不多:getOrCreateTRansversalEntity返回IdentifiedBusinessTransversalEntity子類,可以實現IdentifiedEntity。 最奇怪的是,第一次返回並沒有問題,這是完全對稱的。有什麼不同?

+0

我再補充一點,編譯器錯誤是: – Javaddict

+10

你能使其更簡單?簡單的名稱,完整的代碼,可重複的? – Amit

+0

你使用什麼編譯器? – Lii

回答

0

我會給它一個去。這個解釋是基於觀察而不是顯性知識,所以如果我糾正了,不要感到驚訝。我認爲你很困惑的是<T extends IdentifiedBusinessDSEntity> T返回類型聲明的含義。在我看來,這個返回聲明是創建一個全新的類型,不描述已經聲明的類型。換句話說,你可以使Class1爲Class1 extends ClassA,但是當你返回T extends ClassA時,你是而不是而不是描述Class1,而是一個全新的類(或類型)。您不能指望這個新類與Class1相同,而是說您要返回一個ClassX,其中ClassX extends ClassA:Class1和ClassX不是相同的類。

它看起來像你試圖通過緩存實現工廠設計模式。這段代碼實現了這一點,應該與上面的操作非常相似。假設工廠應該生成接口實現,則不需要將返回類型聲明爲擴展接口的類,只需返回接口即可。

public class Factory { 

    interface I {} 

    List<I> iCache = new ArrayList<I>(); 

    abstract class ClassA {} 
    abstract class ClassB {} 

    class Class1 extends ClassA implements I {} 
    class Class2 extends ClassB implements I {} 

    I getOrCreateTypeA() { 
     for(I cls: iCache) { 
      if(cls instanceof ClassA) return cls; 
     } 
     Class1 cls = new Class1(); 
     iCache.add(cls); 
     return cls; 
    } 
    I getOrCreateTypeB() { 
     for(I cls: iCache) { 
      if(cls instanceof ClassB) return cls; 
     } 
     Class2 cls = new Class2(); 
     iCache.add(cls); 
     return cls; 
    } 

    I getOrCreate(Class<?> cls) { 
     if (ClassA.class.isAssignableFrom(cls)) { 
      return getOrCreateTypeA(); 
     } else if (ClassB.class.isAssignableFrom(cls)) { 
      return getOrCreateTypeB(); 
     } 
     return null; 
    } 

    void run() { 
     I classI1 = getOrCreate(Class1.class); 
     System.out.println(classI1); 
     I classI2 = getOrCreate(Class2.class); 
     System.out.println(classI2); 
     I classI3 = getOrCreate(Class1.class); 
     System.out.println(classI3); 
     System.out.println(iCache); 
    } 

    public static void main(String... args) { 
     new Factory().run(); 
    } 
} 
1

該代碼不應編譯。

我相信你要麼犯了一個錯誤,導致你認爲它有效或者你的編譯器有bug。在Eclipse 4.5.1和javac 1.8.0_45中,getOrCreate中的兩個return語句都給我提供了編譯錯誤。

說明

編輯:我改變了這樣的解釋,我想我誤解了問題的拳頭。

在說明我改變的getAndCreate類型參數的名稱C,以避免與其它類型的參數混淆。

問題是在asSubclass方法中,類是C的子類的信息丟失;唯一剩下的信息是該類是例如IdentifiedBusinessDSEntity的子類。

asSubclass有以下類型:

<U> Class<? extends U> asSubclass(Class<U> clazz) 

我們可以看到接收機類,T的原始類型的參數,是不存在的返回類型。

getOrCreate被宣告返回C。這就是您需要演員陣容的原因:要將C類型重新引入返回值。

替代asSubclass

我們可以想像,asSubclass有以下類型:

<U> Class<? extends U & T> asSubclass(Class<U> clazz) 

這將是類型安全,並與返回類型代碼將編譯沒有轉換。但是Java中不允許使用像這樣的邊界的多個類型參數。

代碼

以下是我用來研究該問題的代碼:

class Test { 
    interface IdentifiedEntity {} 
    class BusinessDSEntity {} 
    class BusinessTransversalEntity {} 

    private <T extends IdentifiedBusinessTransversalEntity> T getOrCreateTransversalEntity(Class<T> classT, String id) { 
     return null; 
    } 

    private <T extends IdentifiedBusinessDSEntity> T getOrCreateDSEntity(Class<T> classT, String id) { 
     return null; 
    } 

    public abstract class IdentifiedBusinessDSEntity 
     extends BusinessDSEntity implements IdentifiedEntity {} 

    public abstract class IdentifiedBusinessTransversalEntity 
     extends BusinessTransversalEntity implements IdentifiedEntity {} 

    public <C extends IdentifiedEntity> C getOrCreate(Class<C> classT, String id) { 
     if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT)) { 
      // Error here. Note that the type of s does not contain C. 
      Class<? extends IdentifiedBusinessDSEntity> s = classT.asSubclass(IdentifiedBusinessDSEntity.class); 
      return getOrCreateDSEntity(s, id); 
     } else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT)) { 
      // Also error here 
      return getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class), id); 
     } 
     return null; 
    } 
} 

可能的解決方法

我不認爲保留了大部分類型安全的任何真正優雅的解決方案。

一種替代方法是將返回類型的類傳遞給實體特定的方法。這相當難看且不方便,您必須手動驗證該類是否屬於正確的類型。

即使您丟失了某些類型的安全性,也可能只是施放返回值。

例子:

class Test { 
    interface IdentifiedEntity {} 
    class BusinessDSEntity {} 
    class BusinessTransversalEntity {} 

    private <R extends IdentifiedEntity, T extends IdentifiedBusinessDSEntity> 
     R getOrCreateDSEntity(Class<T> classT, Class<R> classR, String id) 
    { 
     // Verify that classT really is subclass of classR. 
     classT.asSubclass(classR); 
     return null; 
    } 

    private <R extends IdentifiedEntity, T extends IdentifiedBusinessTransversalEntity> 
     R getOrCreateTransversalEntity(Class<T> classT, Class<R> classR, String id) 
    { 
     // Verify that classT really is subclass of classR. 
     classT.asSubclass(classR); 
     return null; 
    } 

    public abstract class IdentifiedBusinessDSEntity 
     extends BusinessDSEntity implements IdentifiedEntity {} 

    public abstract class IdentifiedBusinessTransversalEntity 
     extends BusinessTransversalEntity implements IdentifiedEntity {} 

    public <C extends IdentifiedEntity> C getOrCreate(Class<C> classT, String id) { 
     if (IdentifiedBusinessDSEntity.class.isAssignableFrom(classT)) { 
      return getOrCreateDSEntity(classT.asSubclass(IdentifiedBusinessDSEntity.class), classT, id); 
     } else if (IdentifiedBusinessTransversalEntity.class.isAssignableFrom(classT)) { 
      return getOrCreateTransversalEntity(classT.asSubclass(IdentifiedBusinessTransversalEntity.class), classT, id); 
     } 
     return null; 
    } 
} 
相關問題