Java interfaces and return types

36,271

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(); }
Share:
36,271
Admin
Author by

Admin

Updated on November 13, 2020

Comments

  • Admin
    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
    Cam about 14 years
    Not 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
    dimitarvp about 14 years
    off-topic: syntax like this is why I am doing my best to avoid creating generified API as much as possible.
  • dimitarvp
    dimitarvp about 14 years
    I 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
    Cam about 14 years
    Good 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
    meriton about 14 years
    The 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
    ARK over 8 years
    @Matt McHenry: Why are you specifying nested generic types?
  • ARK
    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
    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
    Matt McHenry over 6 years
    That last reference to A is a raw type. That should be avoided in anything except legacy code.
  • init_js
    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 like class Cat implements A<String> { ... } would be allowed. The inention is to have T 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/implements A<T>, so we can add that to the constraints.
  • init_js
    init_js about 2 years
    @MattMcHenry nice answer. ThegetThis idea is a nice touch. What's your take on calling the T getThis() method T getThisA() instead to prevent name clashes if two of these interfaces are implemented on the same class? It's tempting to make the getThis a default method: default T getThis() { return (T) this; }.
  • Matt McHenry
    Matt McHenry about 2 years
    I 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.)