Is it possible to specify a method which returns a object that impl开发者_StackOverflowements two or multiple interfaces?
Say we have the following interfaces:
interface FooBar {
[Foo] & [Bar] getFooBar();
}
interface Foo {
void doFoo();
}
inteface Bar {
void doBar();
}
Implementors of FooBar
need to provide the method getFooBar()
that returns an instance of a type which fullfills Foo
as well as Bar
.
What I tried so far is to do it with generics:
interface FooBar {
<T extends Foo & Bar> T getFooBar()
}
class SomeImplementor implements FooBar {
private FooAndBarImpl fSomeField;
public <T extends Foo & Bar> T getFooBar() {
return fSomeField;
}
}
Given that FooAndBarImpl
is some type provided by a framework or library and implements Foo
and Bar
this I think should work. However, it doesn't, because "FooAndBarImpl cannot be converted to T". Why is that? The contract implied by getFooBar()
is not broken as I see it.
Another solution would be to define a new interface that extends Foo
and Bar
and to use that as return type. I just don't see much sense in returning a empty wrapper for the fSomeField
in the getFooBar()
implementation.
EDIT:
Would appreciate it if someone could explain why the generics approach doesn't work. I just don't see it.
You can make T a class parameter:
class SomeImplementor<T extends Foo & Bar> implements FooBar {
private T fSomeField;
public T getFooBar() {
return fSomeField;
}
}
As to why your generics approach didn't work. Lets create the following two classes that implement Foo
and 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;
}
}
So we should now be able to execute the following:
SomeImplementor s = new SomeImplementor();
A a = s.getFooBar();
B b = s.getFooBar();
Although getFooBar()
returns an object of type A, which has no valid cast to type B (where will the String
member come from?), even though B fulfills the requirement of <T extends Foo & Bar>
, i.e. is a valid T
.
In short, the compiler (remember, generics is a compile-time mechanism) can't guarantee that every T
of type <T extends Foo & Bar>
can have an assignment to it of type A
. Which is exactly the error you see - the compiler can't convert the given A to every valid T.
Another solution would be to define a new interface that extends Foo and Bar and to use that as return type.
I would say go for this option.
interface FooBar extends Foo, Bar {
FooBar getFooBar();
}
You could return a container to provide Foo and Bar.
public class Container{
private FooBarBam foobar;
public Bar asBar(){
return foobar;
}
public Foo asFoo(){
return foobar;
}
}
This way your code would not have to implement a third interface. Downside is that it is an additional layer of indirection.
As for why the generic approach does not work: there is no way to provide the type of T and the compiler can't just guess its type, so resolving T is not possible.
davin's answer looks good but also requires a public class/interface which implements Foo and Bar to work.
Update:
The problem is that the compiler does not know that the type of T should be FooAndBarImpl, it would have to guess and a guessing compiler leads to bad and unpredictable code.
Even a hack using Lists wont compile since the & operator is not supported. While it should be possible to implement it looks like generics currently don't support multiple bounds within return types.
//Does not compile expecting > after Foo
List<? extends Foo & Bar> getFooBar(){
final List<FooAndBarImpl> l = new ArrayList<FooAndBarImpl>();
l.add(new FooAndBarImpl());
return l;
}
精彩评论