【问题标题】:Method return type to fulfill multiple interfaces实现多个接口的方法返回类型
【发布时间】:2011-01-20 12:48:43
【问题描述】:

是否可以指定返回实现两个或多个接口的对象的方法?

假设我们有以下接口:

interface FooBar {
    [Foo] & [Bar] getFooBar();
}

interface Foo {
    void doFoo();
}

inteface Bar {
    void doBar();
}

FooBar 的实现者需要提供getFooBar() 方法,该方法返回一个类型的实例,该类型满足FooBar

到目前为止,我尝试的是使用泛型:

interface FooBar {
    <T extends Foo & Bar> T getFooBar()
}

class SomeImplementor implements FooBar {
    private FooAndBarImpl fSomeField;

    public <T extends Foo & Bar> T getFooBar() {
        return fSomeField;
    }

}

鉴于FooAndBarImpl 是框架或库提供的某种类型,并实现了FooBar,我认为这应该可以工作。但是,它没有,因为“FooAndBarImpl 无法转换为 T”。这是为什么? getFooBar() 所暗示的合同在我看来并没有被破坏。

另一种解决方案是定义一个扩展FooBar 的新接口并将其用作返回类型。我只是认为在getFooBar() 实现中为fSomeField 返回一个空包装器没有多大意义。

编辑:

如果有人能解释为什么泛型方法不起作用,将不胜感激。我只是没看到。

【问题讨论】:

  • 在你更新后更新了我的答案,包括(我认为)对泛型问题的一个很好的解释

标签: java generics inheritance interface return-value


【解决方案1】:

你可以让 T 成为类参数:

class SomeImplementor<T extends Foo & Bar> implements FooBar {
    private T fSomeField;

    public T getFooBar() {
        return fSomeField;
    }

}

至于为什么您的泛型方法不起作用。让我们创建以下两个实现FooBar 的类:

class A implements Bar, Foo{
   private int a;
   ...
}
class B implements Bar, Foo{
   private String b;
   ...
}
class SomeImplementor implements FooBar {
   private A someField;
   public <T extends Foo & Bar> T getFooBar() {
      return someField;
   }
}

所以我们现在应该可以执行以下操作了:

SomeImplementor s = new SomeImplementor();
A a = s.getFooBar();
B b = s.getFooBar();

虽然getFooBar()返回了一个A类型的对象,它没有有效地转换为B类型(String成员来自哪里?),即使B满足&lt;T extends Foo &amp; Bar&gt;的要求,即是一个有效的T.

简而言之,编译器(记住,泛型是一种编译时机制)不能保证每个&lt;T extends Foo &amp; Bar&gt; 类型的T 都可以有一个@987654332 类型的赋值给它@。这正是您看到的错误 - 编译器无法将给定的 A 转换为每个有效的 T。

【讨论】:

    【解决方案2】:

    另一种解决方案是定义一个扩展 Foo 和 Bar 的新接口并将其用作返回类型。

    我会说选择这个选项。

    【讨论】:

    • 并创建一个包装器,因为类型是由框架定义的?
    • 不确定包装器是什么意思?如果fSomeField 是实现新组合 FooBar 接口的实例,为什么要使用包装器?
    • 它的类型是由库提供的,即,我无法更改代码。
    • 这个问题是,在 OP 的情况下,已经有类/接口子类 FooBar,所以如果 OP 创建了一个扩展 Foo 和 @ 的接口987654325@,已经存在的接口(不扩展OP的接口)无法从OP的方法返回。
    【解决方案3】:
    interface FooBar extends Foo, Bar {
        FooBar getFooBar();
    }
    

    【讨论】:

      【解决方案4】:

      您可以返回一个容器来提供 Foo 和 Bar。

      public class Container{
         private FooBarBam foobar;
         public Bar asBar(){
            return foobar;
         }
         public Foo asFoo(){
            return foobar;
         }
      }
      

      这样您的代码就不必实现第三个接口。缺点是它是一个额外的间接层。

      至于为什么泛型方法不起作用:没有办法提供 T 的类型,编译器也不能只猜测它的类型,因此无法解析 T。

      davin 的答案看起来不错,但也需要一个实现 Foo 和 Bar 的公共类/接口才能工作。

      更新:

      问题是编译器不知道 T 的类型应该是 FooAndBarImpl,它必须猜测,而猜测编译器会导致错误和不可预测的代码。

      由于不支持 & 运算符,即使是使用列表的 hack 也无法编译。虽然应该可以实现,但看起来泛型目前不支持返回类型中的多个边界。

      //Does not compile expecting > after Foo
      List<? extends Foo & Bar> getFooBar(){
          final List<FooAndBarImpl> l = new ArrayList<FooAndBarImpl>();
          l.add(new FooAndBarImpl());
          return l;
      } 
      

      【讨论】:

      • 这就是我不明白的:编译器知道fSomeField 的类型实现了FooBar,因此应该能够推断它是Foo 的子类型和Bar,不是吗?
      猜你喜欢
      • 1970-01-01
      • 2016-05-15
      • 2011-05-16
      • 2011-06-22
      • 2016-01-09
      • 2019-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多