Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I refer to the type of the current class?

Tags:

java

oop

generics

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?

like image 690
pr00thmatic Avatar asked Aug 03 '13 21:08

pr00thmatic


People also ask

Which of the following can be used to refer the current object?

Summary. this Keyword in Java is a reference variable that refers to the current object. this in Java is a reference to the current object, whose method is being called upon. You can use “this” keyword to avoid naming conflicts in the method/constructor of your instance/object.

What is the current object in Java?

Within an instance method or a constructor, this is a reference to the current object — the object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by using this .


1 Answers

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().

like image 187
Hans Brende Avatar answered Nov 01 '22 19:11

Hans Brende