3

假设我有这个界面:

interface Foo<T extends Foo<T>> { // ... }

以及以下三个实现它的类:

class TrueFoo  implements Foo<TrueFoo > { // ... }

class FalseFoo implements Foo<FalseFoo> { // ... }

class WrongFoo implements Foo<FalseFoo> { // ... }
//                            ^^^^^^^^ this here is wrong, only 
//                                     Foo<WrongFoo> should be allowed

以下方法的方法签名需要什么来强制我可以返回 TrueFoo 或 FalseFoo 类型的对象,但不能返回 WrongFoo...

public ???? getFoo(boolean which) {
    return which ? new TrueFoo() : new FalseFoo();
}

以下不起作用:

  • 这使我还可以返回一个新的 WrongFoo():

    public Foo<?> getFoo(boolean which) {
        return which ? new TrueFoo() : new FalseFoo();
    }
    
  • 这不允许我返回任何东西(解释如下)

    public <FooType extends Foo<FooType>> FooType getFoo(boolean which) {
        return which ? new TrueFoo() : new FalseFoo();
    }
    

    问题是 FooType 的类型是由我调用函数的上下文决定的,getFoo()而不是由我实际尝试返回的对象决定的。

顺便说一句,在调用方法时强制执行此签名很容易:

public <FooType extends Foo<FooType>> void test(FooType foo) {
    // Do something with foo
}

如果您尝试使用 WrongFoo 的实例调用它,这将给您一个绑定不匹配错误。

让我认为应该有一种方法可以对方法返回类型执行此操作。

或者有没有办法在 Foo 的接口定义中强制执行这个签名?

编辑:

为了帮助可视化这个界面应该做什么,你可以考虑这个版本:

interface CanCopy<Type extends CanCopy<Type>> {
    public Type copy();
}

显然, like 类Foo implements CanCopy<Bar>没有任何意义,只有Foo implements CanCopy<Foo>or Bar implements CanCopy<Bar>

编辑2:

正如存在解决方案的概念验证一样,我可以为自己定义以下帮助程序类:

class FooResult {
    private final Foo<?> foo;

    public <FooType extends Foo<FooType>> FooResult(FooType foo) {
        this.foo = foo;
    }

    @SuppressWarnings("unchecked")
    public <FooType extends Foo<FooType>> FooType getFoo() {
        return (FooType) foo;
    }
}

然后我可以要求我的 getFoo 方法是这种类型:

public FooResult getFoo(boolean which) {
    return which ? new FooResult(new TrueFoo()) : new FooResult(new WrongFoo());
}

这样,我将无法从我的 get 方法返回 WrongFoo。

但是,很明显,这有点太复杂了,不能像 Java 泛型通常倾向于编写代码那样优雅(根据我的经验)......那么,这可以以某种方式缩短吗?

编辑 3:

我找到了另一种方法来为实现接口的人提供检查,方法是这样定义:

interface Foo<FooType extends Foo<FooType>> { 
    // Other interface definitions

    /**
     * Please implement with just this line in it:<br/><br/>
     * &nbsp;&nbsp;&nbsp;&nbsp;<code>return this;</code>
     * @return <code>this</code>
     */
    public FooType returnThis();
}

现在,如果有人试图实现上述 WrongFoo 类,他将必须提供该方法FalseFoo returnThis(),如果他只是return this按照 doctype 中的要求实现它,该行将引发错误。

这不是保证,但它是一个非常好的虚拟检查,可以防止因粗心复制类而产生的错误......而且在无论如何都需要带有此签名的消息的情况下,这将是一个很好的解决方案。

还有什么想法吗?

4

1 回答 1

3

您遇到的泛型问题是症状,而不是问题:您要求您的界面依赖于您的具体实现。如果您希望 getFoo 接口方法返回不是 WrongFoo 的内容,则必须返回 TrueFoo 或 FalseFoo 但不是 WrongFoo 的内容。您的类层次结构没有这种特定的间接性。

要添加它,您需要执行以下操作:

class TrueFoo extends SpecialFoo {...}

class FalseFoo extends SpecialFoo {...}

class WrongFoo implements Foo<WrongFoo> {...}

class SpecialFoo implements Foo<SpecialFoo> {  
  SpecialFoo getFoo() {...}
}

请记住,Java 支持协变返回类型,因此通过返回 SpecialFoo 我们可以实现在 Foo 接口中创建的协定:Foo getFoo() {...}

如果您需要特定于 TrueFoo 和 FalseFoo 的行为,您可以进一步参数化 SpecialFoo,等等,海龟一直向下

编辑:

阅读您的编辑后,我不相信您要完成的工作得到该语言的支持。似乎您想根据具体实现来约束接口,根据定义,这将使其不是接口。我认为以下示例与您要查找的内容一样接近:

interface CanCopy<T extends CanCopy<T>> {
       T copy();
}

interface FooCanCopy extends CanCopy<Foo> {
}

interface BarCanCopy extends CanCopy<Bar> {
}

class Foo implements FooCanCopy {

    public Foo copy() {
        return null;
    }
}

class Bar implements BarCanCopy {

    public Bar copy() {
        return null;
    }
}

我希望现在清楚为什么这种方法不起作用,即使在这里你仍然不能阻止某人做类似的事情class Baz implements FooCanCopy。我会更想知道你为什么要这样做?如果它是为了保护开发人员不犯错误,可能还有其他选择,即内省单元测试或打包更改。

于 2012-11-30T02:24:01.840 回答