13

This is kind of difficult to explain, but I've looked everywhere, and I couldn't find any good answer.

I've also seen Stack Overflow questions How can I refer to the class type a interface is implementing in Java? and How do I return an instance of an object of the same type as the class passed in using Java 6?, but they couldn't answer my question. There is an exception when I apply inheritance.

There is an example, to make it easier to understand:

Let's say I have some interface called SelfMaker:

public interface SelfMaker <SELF>{
    public SELF getSelf();
}

And A have a Dog, which can procreate with another dogs. So the dog is a "SelfMaker", like this:

public class Dog implements SelfMaker<Dog> {
    String color;

    public String toString() {
        return "some " + color + " dog";
    }

    public Dog procreate(Dog anotherDog) {
        Dog son = getSelf();
        son.color = color;
        return son;
    }

    @Override
    public Dog getSelf() {
        return new Dog();
    }
}

But then, I have a DomesticDog, who is a Dog, but it has a lovely family who named him. Like this:

public class DomesticDog extends Dog {
    private String name;

    public String toString() {
        return super.toString() + " named " + name;
    }
}

Now, I have some class that handles couples of things that are "SelfMaker"s, let's call this class "Couple". Like this:

public class Couple<T extends SelfMaker<T>> {
    private T first;
    private T second;

    public String toString() {
        return first.toString() + " and " + second.toString();
    }
}

THE EXCEPTION:

The exception comes when I want to create a couple of DomesticDogs. Like this:

public class CoupleOfDomesticDogs extends Couple<DomesticDog>{
    public DomesticDog procreate(){
        DomesticDog son = first.procreate(second);
        return son;
    }
}

This will throw an exception on <DomesticDog> complaining: Bound mismatch: The type DomesticDog is not a valid substitute for the bounded parameter <T extends SelfMaker<T>> of the type Couple<T>

I have already tried to change the generalised variable from class Couple to this: Couple<T extends SelfMaker<?>> but the "son" won't be a DomesticDog (and I want the "son" to be a DomesticDog). If I add some cast, then it will compile, but it will be less legible.

So... here is the question: Is there a way to achieve this without castings and generalizations?

Community
  • 1
  • 1
pr00thmatic
  • 815
  • 9
  • 14
  • related: [Is there a way to refer to the current type with a type variable?](http://stackoverflow.com/questions/7354740/is-there-a-way-to-refer-to-the-current-type-with-a-type-variable) – Paul Bellora Aug 04 '13 at 00:43
  • The Class of the current class is this.getClass(). The class name is this.getClass().getName(). That's as good as it gets. And there's no way to dynamically specify a cast in Java. – Hot Licks Aug 04 '13 at 03:54

2 Answers2

8

There is no way that I can think of to do this without casting. Your problem will be solved if you override the procreate and getSelf methods of DomesticDog and change the declaration of class Couple as such:

public class DomesticDog extends Dog {
    private String name;

    public DomesticDog procreate(Dog anotherDog) {
        return (DomesticDog)super.procreate(anotherDog);
    }

    public Dog getSelf() {
        return new DomesticDog();
    }

    public String toString() {
        return super.toString() + " named " + name;
    }
}

public class Couple<T extends SelfMaker<? super T>> {
    protected T first;
    protected T second;

    public String toString() {
        return first.toString() + " and " + second.toString();
    }
}

If you don't want to override getSelf() in every subclass of Dog, you could make the following change in class Dog:

public Dog getSelf() {
    Class<? extends Dog> thisClass = this.getClass();
    try {
        return thisClass.newInstance();
    } catch (InstantiationException e) {
    } catch (IllegalAccessException e) {
    }
    throw new UnsupportedOperationException(thisClass 
                         + " does not supply a public no-arg constructor");
}

This guarantees that every value returned by getSelf() is an instance of this.getClass(). But you would still have to cast the return value of procreate() for subclasses. There is no way to explicitly specify a return type as this.getClass().

Hans Brende
  • 7,847
  • 4
  • 37
  • 44
  • that's pretty clever. But... isn't there a way to refer to the type of the class? that would make my code way more suitable – pr00thmatic Aug 03 '13 at 23:36
  • 2
    Nope, you can't explicitly refer to the runtime class of "this" with generics unless you use wildcards or casting. See my edit above. – Hans Brende Aug 04 '13 at 00:36
  • All of these ideas circumvent the type system and rely on the programmer to do the right thing without the compiler being able to check it. – newacct Aug 05 '13 at 09:13
  • @newacct, there is no way to avoid this. Given your solution below, what's preventing a programmer from subclassing DomesticDog and not overriding getSelf()? Or creating a subclass of Dog called WildDog and then creating a subclass of WildDog, in which getSelf() is not overridden? The only way to avoid this would be to ensure that all subclasses of Dog are final, but there is no way to programatically specify this in class Dog. Any way you cut the cake, you're going to have to rely on the programmer to do the right thing. – Hans Brende Aug 06 '13 at 06:22
  • @HansBrende: Someone could subclass DomesticDog and not override getSelf(), but then the code would still be perfectly type-safe -- the procreate method would return DomesticDog, which matches what the type parameter says. So if somebody wanted to make a weird subtype of dog that procreates to DomesticDog in a safe way, then great for them; it's equally usable with the code. – newacct Aug 06 '13 at 08:44
  • @newacct True, but the question "How can I refer to the type of the current class?" implies that he wants the return value of getSelf() to be an instance of this.getClass(). If you wanted to specify this in a totally type-safe manner, then every non-abstract subclass would have to be final. Since there is no way to reinforce this, I think the best bet is to just use a cast here and there. This way, you can create Dog instances as well, which you can't do if Dog is abstract, as well as limiting the number of superfluous abstract classes you have to create. – Hans Brende Aug 06 '13 at 11:31
0

You will have to make Dog generic and abstract, with a type parameter that indicates the result of getSelf(). Each type of Dog will then need to implement this with themselves as the parameter:

public abstract class Dog<T> implements SelfMaker<T> {
    String color;

    public String toString() {
        return "some " + color + " dog";
    }

    public T procreate(T anotherDog) {
        T son = getSelf();
        son.color = color;
        return son;
    }

}

public class DomesticDog extends Dog<DomesticDog> {
    private String name;

    public String toString() {
        return super.toString() + " named " + name;
    }

    @Override
    public DomesticDog getSelf() {
        return new DomesticDog();
    }
}
newacct
  • 119,665
  • 29
  • 163
  • 224
  • But what if you wanted to make Dog objects as well? Or what if you wanted to extend DomesticDog to make a subcategory of DomesticDogs? – Hans Brende Aug 06 '13 at 06:05