接口的泛型化导致编译错误。

4

Java 7

我有一个接口:

public interface MyInt{
    public Map<String, WhereClause> createClauses(Parameters<Object> params);
}

以及一些实现:

public class MyImpl implements MyInt{

    @Override
    public Map<String, WhereClause> createClauses(Parameters<Object> p) { //1
        //return some
    }
}

现在,我需要将其返回类型改为通用的。我尝试了以下代码:

public interface MyInt <T extends SomeType>{
    public Map<String, ? extends WhereClause> createClauses(Parameters<Object> params);
}

但是在实现中,我遇到了编译时错误,错误出现在//1处:

The method createClauses(Parameters<Object>) of type `MyImpl` must
override or implement a supertype method

但是,当我移除泛型定义时,实现可以编译通过。

为什么即使没有使用类型参数,泛型化仍会影响编译?


2
你所说的“通配符(?)”是指“泛型”吗? - xehpuk
在新版本中,你仍然写public class MyImpl implements MyInt,还是写implements MyInt<Something>?如果是前者,那么问题是使用原始类型的结果,尽管我不完全理解。如果你不使用原始类型MyInt,它可以正常工作。 - Paul Boddington
@PaulBoddington 是的,没错。但是我对这个错误感到困惑。我们可以使用原始类型而不会有任何编译错误...出了什么问题? - St.Antario
@xehpuk 不,我指的是确切的泛型。因为在我的情况下,泛型化会影响编译,而不是通配符化。 - St.Antario
将“? extends WhereClause”放入接口方法中并尝试。 - Bilbo Baggins
2
我的理解是,为了协助从非泛型代码向泛型代码的过渡,Java 还允许使用未经参数化的原始类型。原始类型通过简单地忽略类的所有类型信息来工作,即使它似乎与使用的原始类型毫不相干(这是确保 Java 4 代码能够正常工作的最简便规则)。由于您使用了原始类型 MyInt,因此方法参数中的类型参数 <Object> 将被完全忽略!因此该方法不会覆盖接口方法。我认为这就是解释,但由于我的理解还有些模糊,所以我不会将其写成答案。 - Paul Boddington
2个回答

5
在几乎所有情况下,当您在泛型中使用 ? 时,您都是错误的:
public interface MyInt<C extends WhereClause> {

    public Map<String, C> createClauses(Parameters<Object> params);
}

private static class MyWhereClause extends WhereClause {

    public MyWhereClause() {
    }
}

public class MyImpl implements MyInt<MyWhereClause> {

    @Override
    public Map<String, MyWhereClause> createClauses(Parameters<Object> p) {
        return null;
    }
}

实际上听起来很合理... 在泛型中使用通配符似乎不是一个好主意。 - St.Antario

1
通常情况下,当一个接口进行重大改动时,比如修改其类型参数,如果子类型没有跟着进行重构,那么它们很可能会立即中断。
但是有一个例外——如果一个接口没有使用泛型,我们可以为其添加泛型并仍然保持先前编写的子类型兼容。当然,这就是所谓的“迁移兼容性”。它能够在不影响旧程序的情况下顺利地工作,例如核心API的 Collection 已经实现了泛型而不破坏旧程序。(- 这里还有另一种语言特性"feature",旨在保持迁移兼容性)
10年后,这个兼容性特性变得越来越像诅咒。您的用例看起来足够合法,但语言本身并未预见到这种情况(即先前非泛型接口已经使用了泛型签名),因此行为似乎相当令人困惑。同样的混淆也导致其他难题(和编译器错误)出现,例如:对于一个具有非泛型超类型和泛型方法签名的泛型类型擦除的影响是什么等等。
在这一点上,我们应该简单地忘记这个特性。尽可能避免使用原始类型,不要尝试将非泛型接口进行泛型化(除非您可以重构子类型)。
你可以引入一个新类型,interface MyInt2<T> extends MyInt,这样现有的MyInt子类型不会受到影响。

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