泛型类型错误:实例无法转换为自己的类型?

时间:2015-01-20 19:13:59

标签: java generics

我从以下代码段收到编译器错误。错误发生在指示的行:

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枚举类提供,因此代码中存在的强制转换是安全的。

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

编译器报告&#34;不兼容的类型:AbstractRoot<R>无法转换为R,其中R是类型变量:R extends Root<R>中声明的AbstractRoot。&#34;

我很困惑。 AbstractRoot被声明为实现Root<R>,因此它应该与为&#34; extends Root<R>&#34;定义的参数兼容。我没有看到指示的构造函数调用和类定义之间存在冲突。

任何人都能帮助我解决这个难题吗?我被卡住了。


更新:感谢下面给出的答案,我得到了我错的地方。 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 个答案:

答案 0 :(得分: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}}

答案 1 :(得分: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
}

换句话说,没有任何内容表明Rthis的类型相同。

这样的自我打字通常会适得其反。通常只需使用更通用的类型Root<R>即可。如果你真的想要这个,一个技巧就是间接获得对this的引用:

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

你在Root<R>声明的地方:

 R typedThis();

并且具体类的实现通常只是:

 public MyRootA typedThis() {
    return this;
 }