Self-Referential Generics in Java
Tags: java
Published: 2020-06-08 18:30:56

---
title: Self-Referential Generics in Java
published: true
description: Why self-referencing generic types are not as useful as they appear.
tags: java
//cover_image: https://direct_url_to_image.jpg
---

I've been having an ongoing debate with colleagues about the use of self-referential generics in Java, in which I assert they are unsound, and others continue to believe in their use. By self-referential generics I mean a type defined in terms of itself, e.g.:
```java
interface Foo<T> { ... }

interface Bar extends Foo<Bar> { ... }
```

On the surface this appears useful as it can enforce correct typing of parallel hierarchies. For example, consider a warehouse system which deals with various types of parts. Each part has a type-specific identifier:
```java
class Serial<P> {
    // stores some identifier specific to P
}

interface Part<P> {
    Serial<P> getSerial();
}
```

Whenever we define a part class, we declare that it implements `Part` parameterised with the class itself. This enforces that the serial gets the same type:
```java
class Whatsit implements Part<Whatsit> {
    private final Serial<Whatsit> serial; 

    @Override
    public Serial<Whatsit> getSerial() {
        return serial;
    }
}
```

We can even have a part database which uses a polymorphic method to ensure if you ask for a `Whatsit` you only get a `Whatsit`.
```java
interface PartDatabase {
    <P extends Part<P>> P getPart(Serial<P> serial);
}
```

So far this is all sound, and we can even work with parts generically in this way:
```java
class HomogenousPartCruncher<P extends Part<P>> {
    private final PartDatabase database;

    void crunchParts(List<Serial<P>> serials) {
        serials.forEach(serial -> crunch(database.getPart(serial)));
    }

    void crunch(Part<P> part) {
        // TODO
    }
}
```

Any given instance of `HomogenousPartCruncher` can only work with one type of `Part` however, the type it is instantiated with. The clue's in the name. But as soon as we try to use an Object Oriented style of coding it all falls apart:

```java
class HeterogeneousPartCruncher {
    private final PartDatabase database;

    void crunchParts(List<Serial<? extends Part<?>>> serials) {
        serials.forEach(serial -> crunch(database.getPart(serial)));
    }
    
    void crunch(Part<?> part) {
        // TODO
    }
}
```

We don't know the type of each `Serial` in the list but it's at least a `Part<?>`. We can therefore try to get the actual `Part<?>` from the database using this serial. But the compiler complains with
```
error: method getPart in interface PartDatabase cannot be applied to given types;
        serials.forEach(serial -> crunch2(database.getPart(serial)));
                                                  ^
  required: Serial<P>
  found: Serial<CAP#1>
  reason: inference variable P has incompatible bounds
    equality constraints: CAP#2,CAP#1
    upper bounds: Part<P>
  where P is a type-variable:
    P extends Part<P> declared in method <P>getPart(Serial<P>)
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends Part<?> from capture of ? extends Part<?>
    CAP#2 extends Object from capture of ?
```

It seems to be suggesting that the type bounds in `Serial` needs to be `extends Part<P>`, but if our application uses `Serial`s for things other than `Part`s we can't do this. It also creates a cyclic dependency which is [never an improvement](https://softwareengineering.stackexchange.com/a/12030) to your overall code structure.

The only way to fix this is to pretend that `Part` is not generic at all, and implement `crunchParts()` like this:
```java
    @SuppressWarnings("unchecked")
    void crunchParts(PartDatabase database, List<Serial<? extends Part>> serials) {
        serials.forEach(serial -> crunch(database.getPart(serial)));
    }
```

And in fact everywhere you refer to `Part` in relation to this code, it has to be `Part<?>`, including return types which is a terrible [code smell](https://rules.sonarsource.com/java/type/Code%20Smell/RSPEC-1452). `Part` may as well not be generic at all:

```java
interface Part {
    Serial<? extends Part> getSerial();
}

class Whatsit implements Part {
    @Override
    public Serial<Whatsit> getSerial() { ... }
    }
}

interface PartDatabase {
    <P extends Part> P getPart(Serial<P> serial);
}

class HomogenousPartCruncher<P extends Part> {
    private final PartDatabase database;

    void crunchParts(List<Serial<P>> serials) {
        serials.forEach(serial -> crunch(database.getPart(serial)));
    }

    void crunch(P part) {
        // TODO
    }
}

class HeterogeneousPartCruncher {
    private final PartDatabase database;

    void crunchParts(List<Serial<? extends Part>> serials) {
        serials.forEach(serial -> crunch2(database.getPart(serial)));
    }

    void crunch2(Part part) {
        // TODO
    }
}
```

The code is cleaner and easier to understand, has the same guarantees of type safety, yet doesn't need `<?>` wildcards nor `@SuppressWarnings("unchecked")` everywhere.

The only way to use self-referential generics without annoying wildcards and suppressing unchecked cast warnings is for all - and I mean *all* - your code to work with concrete types only. Which is a distinctly non-object oriented style that I find curious in a language so singularly devoted to promoting Object Oriented programming that class hierarchies are pretty much the only abstraction tool it offers.