是否可以指定一个返回实现两个或多个接口的对象的方法?
假设我们有以下接口:
interface FooBar {
[Foo] & [Bar] getFooBar();
}
interface Foo {
void doFoo();
}
inteface Bar {
void doBar();
}
FooBar
的实现者需要提供方法getFooBar()
,该方法返回满足Foo
以及Bar
的类型的实例。
我到目前为止尝试的是使用泛型:
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
是框架或库提供的某种类型并实现Foo
和Bar
,我认为这应该有用。但是,它没有,因为“FooAndBarImpl无法转换为T”。这是为什么? getFooBar()
隐含的合同并没有像我看到的那样被打破。
另一个解决方案是定义一个扩展Foo
和Bar
的新接口,并将其用作返回类型。我在fSomeField
实现中为getFooBar()
返回一个空包装时没有多大意义。
修改
如果有人可以解释为什么泛型方法不起作用,我将不胜感激。我只是没有看到它。
答案 0 :(得分:14)
你可以使T成为一个类参数:
class SomeImplementor<T extends Foo & Bar> implements FooBar {
private T fSomeField;
public T getFooBar() {
return fSomeField;
}
}
至于为什么你的泛型方法不起作用。让我们创建以下两个实现Foo
和Bar
的类:
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满足{{1}的要求},即有效的<T extends Foo & Bar>
。
简而言之,编译器(记住,泛型是编译时机制)不能保证类型T
的每个T
都可以赋值给它类型为<T extends Foo & Bar>
。这正是您看到的错误 - 编译器无法将给定的A转换为每个有效的T。
答案 1 :(得分:2)
另一种解决方案是定义一个扩展Foo和Bar的新接口,并将其用作返回类型。
我会说选择这个选项。
答案 2 :(得分:2)
interface FooBar extends Foo, Bar {
FooBar getFooBar();
}
答案 3 :(得分:0)
您可以返回一个容器来提供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,它必须猜测并且猜测编译器会导致错误和不可预测的代码。
即使是使用Lists的hack也不会编译,因为&amp;不支持运算符。尽管应该可以实现它,但是泛型当前不支持返回类型中的多个边界。
//Does not compile expecting > after Foo
List<? extends Foo & Bar> getFooBar(){
final List<FooAndBarImpl> l = new ArrayList<FooAndBarImpl>();
l.add(new FooAndBarImpl());
return l;
}