Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bound the class type of the subclass

Tags:

java

generics

I have this class:

public abstract class Addressable {

  abstract <T extends "this" Addressable> void hardEquals(T t);

}

The method hardEquals(T t) is not bounded as I want. What I want is bound T to be same class of this. In other words when I extend the class Addressable, with the concrete class MyAddressable, I want that the method hardEquals() has the signature:

void hardEquals(MyAddressable t);

For completeness:

public class MyAddressable extends Addressable {

  void hardEquals(MyAddressable t);

}
  1. Is it possible to achieve what I want?
  2. If the answer is no, is it so stupid my class constraint?
like image 207
optimusfrenk Avatar asked Feb 18 '16 09:02

optimusfrenk


People also ask

How do you find the class name for a subclass?

The Class object has a getName() method that returns the name of the class. So your displayClass() method can call getClass(), and then getName() on the Class object, to get the name of the class of the object it finds itself in.

Which of these is the correct way to declare a bounded type?

To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number .

How do I restrict a generic type in Java?

Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.

Which class is the subclass?

Definitions: A class that is derived from another class is called a subclass (also a derived class, extended class, or child class). The class from which the subclass is derived is called a superclass (also a base class or a parent class).


4 Answers

It’s not possible in general. The problem becomes clear when think a bit longer about your scenario. You have that class Addressable and its method hardEquals which demands that its parameter has the type equal to this. Then you have the subclass

public class MyAddressable extends Addressable {
  void hardEquals(MyAddressable t);
}

that seems to work as intended. Now imagine a subclass of the subclass:

public class GrandChild extends MyAddressable {
  void hardEquals(MyAddressable t);
}

Here, you have a class which must accept MyAddressable as hardEquals’ parameter as it is not allowed to override a method by narrowing the parameter types, i.e. not accepting things that the superclass accepted. After all, you can always have a variable of type MyAddressable that actually refers to an instance of GrandChild. It would be impossible to restrict the parameters to hardEquals to match the actual runtime type of the object.

So the desired “parameter type must match this type” rule conflicts with the “subclass methods must accept all parameters the superclass did” rule.


Note that this is often confused with an actual limitation of the type system, that the same thing doesn’t work for return types. Since return types are allowed to be narrowed for subclasses, the desire to guaranty to return the this type could be reasonable in some scenarios, e.g.:

class Base {
    Base/*actually this type*/ clone() { … }
}
class SubClass extends Base {
    SubClass/*actually this type*/ clone() { … }
}
class GrandChild extends SubClass {
    GrandChild/*actually this type*/ clone() { … }
}

works, but there is no formal way to specify the guaranty that the this type is returned so it’s up to the programmer’s discipline to add the correct override to every subclass.

But, as said, for parameter types this doesn’t work in general as you can’t narrow the type of a parameter in a subclass.

like image 71
Holger Avatar answered Oct 01 '22 13:10

Holger


You could borrow a trick from Comparable, and specify the generic type in the class declaration itself:

public abstract class Addressable<T> {
    abstract <T> void hardEquals(T t);
}

This results in slightly clunky class definitions, but it should fulfill your requirement

public class HomeAddress extends Addressable<HomeAddress> {
    void hardEquals(HomeAddress t) {
        // implementation
    }
}
like image 32
Mureinik Avatar answered Sep 30 '22 13:09

Mureinik


I'm not aware of a clean solution for your problem.

You could think about something like this:

public abstract class Addressable<T extends Addressable<T>> {

  abstract void hardEquals(T other);

}

public class MyAddressable extends Addressable<MyAddressable> {

  @Override
  void hardEquals(MyAddressable other) {
    // ...
  }

}

But this could fail in some cases, e.g.:

public class FakeAddressable extends Addressable<MyAddressable> {

  @Override
  void hardEquals(MyAddressable aT) {
    // ...
  }

}
like image 31
sp00m Avatar answered Oct 02 '22 13:10

sp00m


If you are looking for a hardEquals that can only be used with objects with exactly the same type then something like this should work:

public abstract class Addressable<T extends Addressable<T>> {

    abstract boolean hardEquals(T t);

}

class X extends Addressable<X> {

    @Override
    boolean hardEquals(X t) {
        return true;
    }

}

class Y extends Addressable<Y> {

    @Override
    boolean hardEquals(Y t) {
        return true;
    }

}

public void test() {
    X x = new X();
    if ( x.hardEquals(x)) {
        System.out.println("Ok");
    }
    Y y = new Y();
    // Fails!
    if ( x.hardEquals(y)) {
        System.out.println("Not OK");
    }
}

As @Holger rightly points out - this does not help when extending the sub-classes.

like image 20
OldCurmudgeon Avatar answered Oct 02 '22 13:10

OldCurmudgeon