【问题标题】:Enforcing Java method return type to fit certain Generic signature强制 Java 方法返回类型以适应某些通用签名
【发布时间】:2012-11-30 01:54:52
【问题描述】:

假设我有这个界面:

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&lt;Bar&gt; 这样的类没有任何意义,只有Foo implements CanCopy&lt;Foo&gt; 或Bar implements CanCopy&lt;Bar&gt;。

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

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

还有什么想法吗?

【问题讨论】:

  • 这是一个有趣的问题,但它确实令人困惑。与其试图如此笼统,不如加入一些真实姓名。那些傻瓜很难跟上
  • @hbtest True...我正在尝试考虑是否有更好的方法可以以这种方式思考...有什么想法吗?
  • 这只是一个笼统的问题,还是您实际上有这种情况?我假设您在某种情况下遇到过这种情况,如果是这样,请发布。
  • 我确实在我的代码中遇到过它,但是上下文非常复杂,我试图尽可能地将问题提炼为实际的问题。
  • 我很难为您提供帮助,因为我自己无法理解它......对不起

标签: java generics methods return-type


【解决方案1】:

您遇到的泛型问题是症状,而不是问题:您要求您的接口依赖于您的具体实现。如果您希望 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 的事情。我会更想知道你为什么要这样做?如果它是为了保护开发人员不犯错误,可能还有其他选择,即内省单元测试或打包更改。

【讨论】:

  • 不幸的是,这在我使用它的情况下不起作用(例如,参见我对原始问题所做的编辑;我会失去副本实际上与原本的)。我不能将我的“Foo”的不同实现浓缩成一个最小公分母超类型,并且让整个东西仍然有用。在我的情况下,这相当于实际上只使用“对象”。
  • 见我的最后一条注释,在这种情况下,您将使用自己的参数对 SpecialFoo 进行参数化,如下所示:class TrueFoo extends SpecialFoo&lt;TrueFoo&gt; 等。所以您的 getFoo 在 @ 中看起来像 SpecialFoo&lt;TrueFoo&gt; getFoo() {...} 987654329@班级。
  • 不幸的是,即使在我的情况下也行不通。我的实际问题确实非常接近(在概念上)我上面描述的 CanCopy 情况提出的要求。
  • 我想出了一些方法来接近我可能希望得到的答案。如果您有时间,请快速查看我对问题的 EDIT 2,看看这是否有帮助?
猜你喜欢
  • 1970-01-01
  • 2010-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-16
  • 2012-06-13
  • 1970-01-01
  • 2014-08-18
相关资源
最近更新 更多