Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

abstract recursive generic causes bound mismatch

In trying to structure my classes in a logical way, I discovered Java's ability to do recursive generics. It is almost exactly what I was looking for structure-wise, but I ran into a problem with abstract classes. I think Foo and Bar would be extremely confusing for this example, so I've named my classes relating to my actual project.

public abstract class GeneCarrier<T extends GeneCarrier<T>> {
    protected Gene<T> gene;
    //...
}

public class Gene<T extends GeneCarrier<T>> {
    //...
}

public abstract class Organism extends GeneCarrier<Organism>{
    //...
}

public class Human extends Organism {
    public void foo(){
        Gene<Human> g; // Bound mismatch: The type Human is not a valid substitute for the bounded parameter <T extends GeneCarrier<T>> of the type Gene<T>
    }
}

I thought that the problem might be with the definition of my abstract Organism class, but this also produced a similar error:

public abstract class Organism extends GeneCarrier<? extends Organism>{
    //...
}

Is there an inherent problem in trying to use an abstract class with recursive template definitions, or have I made a mistake in the class definitions?

like image 408
wrongu Avatar asked Apr 20 '26 17:04

wrongu


1 Answers

Is there an inherent problem in trying to use an abstract class with recursive template definitions, or have I made a mistake in the class definitions?

It looks like you made a mistake. The recursive bound on Gene's type parameter T necessitates that a type argument of Human should mean that Human is a GeneCarrier<Human>. But it isn't - Human is a GeneCarrier<Organism>.

To implement this pattern correctly, the recursive type parameter should be propagated down the inheritance tree until it reaches what I like to call a "leaf" class, which in this case seems to be Human:

public abstract class Organism<T extends Organism<T>> extends GeneCarrier<T> {
    //...
}

public final class Human extends Organism<Human> {
    public void foo(){
        Gene<Human> g; // valid
    }
}

This solves the issue at hand but you should know the ups and downs of using "self-types" in Java (generally known as the Curiously Recurring Template Pattern). I go into detail about implementing this pattern and its pitfalls on this post: Is there a way to refer to the current type with a type variable?

In general I find that developers try to use "self-types" in order to implement a type-safe "copy" method on certain classes (which seems like the case here since your type names are gene-related). When that happens I always recommend trying to decouple the copying responsibility to a separate type, in order to avoid the added complexity of recursive generics. My answer here is an example.

like image 95
Paul Bellora Avatar answered Apr 23 '26 08:04

Paul Bellora



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!