tags:

views:

164

answers:

3

Is there a way to say "this method returns this" using Generics?

Of course, I want to override this method in subclasses, so the declaration should work well with @Override.

Here is an example:

class Base {
    public Base copyTo (Base dest) {
        ... copy all fields to dest ...
        return this;
    }
}
class X extends Base {
    @Override
    public X copyTo (X dest) {
        super.copyTo (dest);
        ... copy all fields to dest ...
        return this;
    }
}

public <T extends Base> T copyTo (Base dest) doesn't work at all: I get "Type mismatch: Can't convert from Base to T". If I force it with a cast, the override fails.

+3  A: 

No, there's no way of expressing that. Just declare the method to return the type of the class. Java has covariant return types, so you can override a method to return a more specific type anyway.

If you wanted to have some marker for this, you could always introduce your own annotation - but don't expect any other tools to take any particular notice of it.

EDIT: the answer from oxbow_lakes does indeed give something which will work in most cases, but I believe there are ways of fooling it such that you're actually dealing with a different type. (From memories of experimentation, anyway.) Note that this is similar to how Java enums work.

Jon Skeet
Apart from my (well, Martin Odersky's) way; detailed above
oxbow_lakes
Yes, although there are ways of implementing that kind of thing without "This" *actually* being This.
Jon Skeet
Well, "This" can be anything which implements the List<A> interface, which because of erasure could be a List of anything. I'd be interested if you could fool it completely
oxbow_lakes
@oxbow_lakes: sounds like an interesting challenge. How about we define it more precisely before I start having a go... are we just trying to create a valid implementation without any raw types being involved, but where the class is Foo and the add method returns a type other than Foo?
Jon Skeet
+3  A: 

You can do something very clever (and akin to what they have done in Scala with the 2.8 collection framework). Declare some interface method that should return "itself" (Note: This is a type parameter, not a keyword!)

public interface Addable<T, This extends Addable<T, This>> {
   public This add(T t);
}

Now declare a level of indirection - a "template" class

public interface ListTemplate<A, This extends ListTemplate<A, This>> 
    extends Addable<A, This>{
}

public interface List<A> extends ListTemplate<A, List<A>> {
}

Then an implementation of List has to return a List from the add method (I'll let you fill in the impl details)

public class ListImpl<A> implements List<A> {

    public List<A> add(A a) {
        return ...
    }
}

Similarly you could have declard a SetTemplate and a Set to extend the Addable interface - the add method of which would have returned a Set. Cool, huh?

oxbow_lakes
Nice. But doesn't work when I extend ListImpl.
Aaron Digulla
It does when I try: SubListImpl<A> extends ListImpl<A> and add returns SubListImpl<A> with no compilation issues at all
oxbow_lakes
How can I force add() to only accept This?
Aaron Digulla
Note that there's a difference between "this" and "This". There is no way in Java of forcing a particular instance to be returned from a method; you can only say that a particular type is to be returned. It is not possible in to force that an overriding method returns a sub-type; it only need return a type which is consistent with the method it overrides.My example was a neat trick using a layer of indirection to make it appear as if a subtype is to be returned. There is only one layer of indirection, however, so it can't be used to further restrict subtypes of ListImpl
oxbow_lakes
A: 

using covariant types should be simple as:

abstract class Foo<T> {

    Foo<T> get() {
        return this.getClass().cast(this);
    }
}

class Bar extends Foo {

    @Override
    Bar get() {
        return (Bar) super.get();
    }
}
dfa
You'd get unchecked type warnings from javac if you did this
oxbow_lakes
javac (jdk6) gives me no warnings at all. I'm missing something?
dfa
You must be - you've extended a generic class (Foo<T>) but not supplied any type parameters (Bar extends Foo)
oxbow_lakes
Possibly you have the --Xl: int unchecked flag on (or
oxbow_lakes
you are right, I'll keep this (wrong) answer for future generations :)
dfa