Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generic type that either extends or is a parent class

Tags:

java

generics

I am looking for some code that would look like below:

public class Parent<T is_or_extends Parent<T>> {
    public T function() {
        // do some stuff //
        return correct();
    }
    public T correct() {
        return (T) this;
    }
}

This is so any child classes will have access to the other parent functions, yet will remain independent classes (without upcasting the return value of function() to an instance of Parent). And the programmer can also use the Parent class independently as well (so an instance of Parent can be created without function() downcasting the return object)

Implementation:

public class Child extends Parent<Child> {}

Usage:

Child child = new Child(); // ok
Parent<Child> polymorph = new Child(); // ok
Parent<Parent> parent = new Parent<Parent>(); // ERROR!

Just to outline a difference, this is not an issue with method chaining. Rather when the parent class takes in a generic of a child class, it can no longer be used as a class in its own right (using the code above), because new Parent<Parent>() will not compile (when replacing "is_or_extends" with "extends")

My Goal:

What I am aiming to achieve is a parent class that when extended, function() will return an object of the child class rather than the parent class.

I would use a generic type to tell the parent class which child class called the function, but I can no longer use an object of the parent class itself without experiencing exceptions. An example is below:

public static class Parent<T extends Parent<T>> {}
public static class Child extends Parent<Child> {}

public static void main(String[] args) {
    // This will not compile as Parent cannot extend Parent
    Parent<Parent> parent = new Parent<Parent>();

    // This will work as Child extends Parent
    Child child = new Child();
}

Thanks in advance.

like image 957
Robert E Fry Avatar asked Feb 07 '23 07:02

Robert E Fry


1 Answers

extends does mean "is or extends". The reason your example doesn't compile is because the inner Parent is a raw type, which is not a valid substitute for T extends Parent<T>. The "correct" type would look like this: Parent<Parent<Parent<...>>> ad infinitum. Obviously such a type is impossible to declare.

One solution is to instantiate it like this:

Parent<?> parent = new Parent<>();
Parent<?> derived = parent.function();

This works because the compiler already knows that T is some subclass of Parent<?>. One disadvantage of this trick is that it won't work if the type can't be inferred, for example when using anonymous classes.

Another possible approach - dependent on the nature of parent/child relationship - is to create an additional class to extend the base, just for its type resolution:

// Parent functionality in here
public static abstract class Base<T extends Base<T>> {}

// This can be empty, or have just constructors if necessary
public static class Simple extends Base<Parent> {}

// Child functionality in here
public static class Extended extends Base<Extended> {}
like image 79
shmosel Avatar answered Mar 06 '23 21:03

shmosel