执行Java方法返回类型以适合某些通用签名

时间:2012-11-30 01:54:53

标签: java generics methods return-type

假设我有这个界面:

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

显然,像Foo implements CanCopy<Bar>这样的课程没有任何意义,只有Foo implements CanCopy<Foo>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 Generics通常倾向于制作代码(根据我的经验)......所以,这可以以某种方式缩短吗?

编辑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(),如果他只是按照doctype中的要求将其实现为return this,那么该行将抛出错误。

这不是一个保证,但它是一个非常好的假检查,可以防止因粗心复制类而导致的错误......而且如果需要带有此签名的消息,那将是一个很好的解决方案。

还有什么想法?

1 个答案:

答案 0 :(得分: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,依此类推,turtles all the way down

修改

阅读完编辑后,我不相信你想要完成的是语言所支持的。看起来你想根据具体的实现来约束接口,根据定义,这将使它不是一个接口。我认为以下示例与您要找到的内容非常接近:

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这样的事情。我更想知道你为什么要这样做?如果它保护开发人员不犯错误,可能还有其他选择,即内省单元测试或包装更改。