2015-01-20 82 views
3

我從下面的代碼段收到編譯器錯誤。在所指示的線發生了錯誤:泛型類型錯誤:實例無法轉換爲其自己的類型?

static abstract class AbstractRoot<R extends Root<R>> implements Root<R> { 

    private final Map<Grouping, Phrase> m_ph; 

    AbstractSqlRoot() { this.m_ph = new HashMap<>(); } 

    @Override public PhraseBuilder<R> phrase() { 
     PhraseBuilder<R> fr = (PhraseBuilder<R>) m_ph.get(Grouping.PHRASE); 
     if (fr == null) { 
-->   fr = new PhraseBuilder<R>(this); 
      m_ph.put(Grouping.PHRASE, fr); 
     } 
     return fr; 
    } 

在HashMap中m_ph是一種類型安全異質容器。類型鏈接由Grouping enum類提供,因此代碼中的轉換是安全的。

類定義爲PhraseBuilder(和它自己的AbstractPhraseBuilder)按照在下面的部分:

public static abstract class AbstractPhrase<R extends Root<R>> implements Phrase { 

    private final List<Element>  m_elem; 
    private final R     m_root; 

    AbstractPhrase(R r) 
      { m_elem = new ArrayList<>(); m_root = r; } 
    : 
    : 
} 


public static final class PhraseBuilder<R extends Root<R>> extends AbstractPhrase<R> { 

    public PhraseBuilder()   { this(null); } 
    public PhraseBuilder(R r)  { super(r); } 
    : 
    : 
} 

編譯器報告「不兼容的類型:AbstractRoot<R>不能轉換到R,其中R是一種類型的變量:R extends Root<R>在課堂上申報AbstractRoot「。

我很困惑。 AbstractRoot被宣佈執行Root<R>,因此它應該與爲「擴展Root<R>」定義的參數兼容。我沒有看到指定的構造函數調用和類定義之間的衝突。

任何能幫助我解決這個難題的人?我卡住了。


UPDATE:由於下面給出的答案,我得到我哪裏錯了。 SqlRoot<R>接口的設計確實需要將R發送到PhraseBuilder類。代碼已修補如下來解決這個問題:

static abstract class AbstractRoot<R extends Root<R>> implements Root<R> { 

    private final Map<Grouping, Phrase> m_ph; 

    AbstractSqlRoot() { this.m_ph = new HashMap<>(); } 

    @Override public PhraseBuilder<R> phrase() { 
     PhraseBuilder<R> fr = (PhraseBuilder<R>) m_ph.get(Grouping.PHRASE); 
     if (fr == null) { 
-->   fr = new PhraseBuilder<R>(typedThis()); 
      m_ph.put(Grouping.PHRASE, fr); 
     } 
     return fr; 
    } 
    : 
    : 
    protected abstract R typedThis(); 
} 

類定義爲子類ConcreteRoot現在包括用於typedThis()方法聲明。事實上,自該方法的理想的解決方案將成爲類的出口API的一部分,但它解決了這個問題:

static final class ConcreteRoot extends AbstractRoot<ConcreteRoot> { 

    ConcreteRoot() { super(); } 

    @Override protected ConcreteRoot typedThis() { return this; } 
    : 
    : 
} 

回答

2

問題是thisRoot<R>,不一定是R。聲音混亂?它是。這裏有一個反哪裏會打破:

public class MyRootA extends AbstractRoot<MyRootA> { 
    //R is MyRootA - "this" is an instance of R 
} 

public class MyRootB extends AbstractRoot<MyRootA> { 
    //R is MyRootA - "this" is NOT an instance of R 
} 

換句話說,沒有什麼,指出R相同類型this

這樣的自我打字往往適得其反。僅使用更通用的類型Root<R>通常就足夠了。如果你真的想這樣,一個關鍵是要獲得一個參考this間接:

fr = new PhraseBuilder<R>(this.typedThis()); 

Root<R>聲明:

R typedThis(); 

,並在具體的類的實現將通常只:

public MyRootA typedThis() { 
    return this; 
} 
+0

謝謝你。不幸的是,當我爲這些東西設計API時,我確實需要在接口'SqlRoot '中返回一個'R'的方法。我看到我出錯的地方。從子類獲得'R'的解決方案在API上留下可見的瑕疵(這個類不是導出的API的一部分),但它允許我繼續前進。謝謝。 – scottb 2015-01-21 13:15:57

4

new PhraseBuilder<R>(this)試圖調用此構造:

public PhraseBuilder(R r)  { super(r); } 

的問題是,this不一個R:這是一個AbstractRoot<R>,這也恰好是一個Root<R>,但它不是一個R。也許你的意思是?

fr = new PhraseBuilder<Root<R>>(this); 
+1

感謝您的明確,有益的,有用的答案。我現在看到我出錯的地方。當我設計這個API時,我真的需要接口'SqlRoot '中的方法來返回一個R.沒有重構大量代碼就沒有辦法解決它。出於這個原因'新的PhraseBuilder >(this)'不能幫助我。我真的需要一個'R'。 – scottb 2015-01-21 13:09:55