Java interfaces and return types
Solution 1
If the return type must be the type of the class that implements the interface, then what you want is called an F-bounded type:
public interface A<T extends A<T>>{ public T b(); }
public class C implements A<C>{
public C b() { ... }
}
public class D implements A<D>{
public D b() { ... }
}
In words, A
is declaring a type parameter T
that will take on the value of each concrete type that implements A
. This is typically used to declare things like clone()
or copy()
methods that are well-typed. As another example, it's used by java.lang.Enum
to declare that each enum's inherited compareTo(E)
method applies only to other enums of that particular type.
If you use this pattern often enough, you'll run into scenarios where you need this
to be of type T
. At first glance it might seem obvious that it is1, but you'll actually need to declare an abstract T getThis()
method which implementers will have to trivially implement as return this
.
[1] As commenters have pointed out, it is possible to do something sneaky like X implements A<Y>
if X
and Y
cooperate properly. The presence of a T getThis()
method makes it even clearer that X
is circumventing the intentions of the author of the A
interface.
Solution 2
Generics.
public interface A<E>{
public E b();
}
public class C implements A<C>{
public C b(){
return new C();
}
}
public class D implements A<D>{
public D b(){
return new D();
}
}
Search up generics for more details, but (very) basically, what's happening is that A
leaves E
's type up to the implementing clases (C
and D
).
So basically A doesn't know (and doesn't have to know) what E might be in any given implementation.
Solution 3
Since Java supports covariant return types (since Java 1.5), you can do:
public interface A { public Object b(); }
Admin
Updated on November 13, 2020Comments
-
Admin over 3 years
Consider I have the following interface:
public interface A { public void b(); }
However I want each of the classes that implement it to have a different return type for the method b().
Examples:
public class C { public C b() {} } public class D { public D b() {} }
How would I define my interface so that this was possible?
-
Cam about 14 yearsNot great. You want your interface to leave as little room for accidental misuse as possible. With a method like that, it would be very easy for the programer to make a mistake when implementing
A
. -
dimitarvp about 14 yearsoff-topic: syntax like this is why I am doing my best to avoid creating generified API as much as possible.
-
dimitarvp about 14 yearsI like that one more than the one marked as "the" answer, because you don't have to rely on the fact that the implementors will actually include their class name in the generified declaration. Thumbs up from me on that one.
-
Cam about 14 yearsGood answer (and a great example of the power of generics), but this doesn't prevent the programmer from doing C implements A<D> (see my second answer).
-
meriton about 14 yearsThe question did not explicitly specify any restrictions on the overriding types. Of course, if such restrictions exist, one should enforce them with the interface where feasible.
-
ARK over 8 years@Matt McHenry: Why are you specifying nested generic types?
-
ARK over 8 years@Matt McHenry: Any specific reason, why you are doing A<T extends A<T>> rather than just A<T>?
-
Jian Guo over 6 years@MattMcHenry What if I declare interface A as
public interface A<T extends A>
? It seems that it still works well with your example. So what's the difference here? -
Matt McHenry over 6 yearsThat last reference to
A
is a raw type. That should be avoided in anything except legacy code. -
init_js about 2 years@ARK @JianGuo I believe the self-referencial notation is to constrain T as much as possible. Consider that if we only had
A<T>
, something likeclass Cat implements A<String> { ... }
would be allowed. The inention is to haveT
close in as much as possible to the type that implements A. What little amount of info do we have on T? ... well we know that it at least extends/implementsA<T>
, so we can add that to the constraints. -
init_js about 2 years@MattMcHenry nice answer. The
getThis
idea is a nice touch. What's your take on calling theT getThis()
methodT getThisA()
instead to prevent name clashes if two of these interfaces are implemented on the same class? It's tempting to make thegetThis
a default method:default T getThis() { return (T) this; }
. -
Matt McHenry about 2 yearsI haven't run across a case where I needed a class to implement two of these kinds of interfaces, but if you did, I think it would actually save boilerplate to have both methods called
getThis()
-- then the implementing class would only need to implement it once. Using a default method is a nice idea -- those didn't exist when I wrote this answer way back in 2010! :) (Though I don't love the unchecked cast.)