通用类型错误:实例不能转换为其自身类型?

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

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

编译器报告:“不兼容的类型:AbstractRoot<R>无法转换为R,其中R是一个类型变量:R extends Root<R>在类AbstractRoot中声明。”
我感到困惑。 AbstractRoot被声明为实现Root<R>,所以它应该与“扩展Root<R>”定义的参数兼容。我没有看到指示的构造函数调用和类定义之间的冲突。
有人能帮我解决这个困境吗?我卡住了。
更新:感谢下面给出的答案,我明白了我的错误所在。 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个回答

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>中的方法返回一个R。除非重构大量代码,否则没有其他办法。因此,new PhraseBuilder<Root<R>>(this)对我没有帮助。我真的需要一个R - scottb

2
问题在于this是一个Root<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;
 }

谢谢这个。不幸的是,当我设计这个东西的API时,我真的非常需要接口SqlRoot<R>中的方法返回一个R。我知道我哪里错了。从子类获取R的解决方案在API上留下了一个可见的瑕疵(无论如何,这个类都不是导出API的一部分),但它让我可以继续前进。谢谢。 - scottb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接