8

NOTE: This question is not Enum-related, so it's not duplicate. Enum's are forced to compare only-with-itself because compiler generation of type parameter, not because java recursive type parameter.

I'm trying to find an advantage for declaring a class as:

public class Some<E extends Some<E>>

versus declaring it as:

public class Some<E extends Some>

I have tried providing methods returning E and methods returning Some<E>, different cross-calls in complicated class hierarchy and every time I've tried to remove additional <E> - no new errors/warnings came up.

Can you show me a method that proves the advantage of this additional <E>? I assume that there exists one because of JDK declarations:<E extends Comparable<? super E>>

Responses to other questions on SO gives for example:

With the additional construct, you know that any class that extends Enum is comparable only against itself

But, I can easily break this theory:

public static class Animal<E extends Animal<E>> {
    public boolean compare(E other) {...}
}

public class Cat extends Animal<Cat> { }
public class Dog extends Animal<Cat> { } // Note "Cat" !!!

Despite the generic recursion, i can still compare Dog with Cat:

Dog dog = new Dog();
dog.compare(new Cat());

Transalting theory:

you know that any class that extends Animal is comparable only against itself

this is false - I comapred class Dog which extends Animal with Cat, not itself.

Kinga Odecka
  • 387
  • 4
  • 13
  • Well, it's the same because it's how generics works (Some == Some after compile). search raw types and info about generics. – Marco Acierno Aug 18 '14 at 14:25
  • Related: [self-bounded-generics](http://stackoverflow.com/questions/19588449/self-bounded-generics) – Pshemo Aug 18 '14 at 14:26
  • @Pshemo: It's actually a duplicate of that question; alas that question has no good answers :( – Oliver Charlesworth Aug 18 '14 at 14:35
  • The problem is, that even in deeper-qustion answer (http://stackoverflow.com/a/3061776/2299391) I just building explained samples, removing `` and no additional warinings/errors :(. Especialy: _"With the additional construct, you know that any class that extends Enum is comparable only against itself"_ -looks to NOT be truth, if I make `Dog extends Animal` - I can compare dog with cat, even if animal declaration is `Animal>` – Kinga Odecka Aug 18 '14 at 14:40
  • 1
    Since the point of OO programming is to make writing and understanding programs easier and faster, and reading this code makes my eyes bleed and my inner child cry, chances are the question is academic; if you ever get into a situation where you feel the need to write `Some>`, you probably made an architectural mistake somewhere in your program. Either that, or you've turned to the Dark Side of intentionally obfuscated Java. – Parthian Shot Aug 18 '14 at 15:04
  • 1
    To duplicate marker - This question has NO answer already in other Stack Overflow questions. Other questions show that **java compiler** improves Enum because it generates it as `extends Enum` but additional recursive type parameter doesn't add a value at this moment (see my answer). – Kinga Odecka Aug 18 '14 at 15:18
  • @ParthianShot See this article, a typical strategy pattern and really improved because of that: http://www.javaspecialists.eu/archive/Issue123.html – Kinga Odecka Aug 18 '14 at 15:22
  • I would go a step further and ask, when is there a need for `Some` or `Some>` instead of just `Some`? – newacct Aug 19 '14 at 07:31
  • While I myself cannot properly wrap my head around it, a possible example for when this was used is in the `Vector` interface in LibGDX: https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/math/Vector.java – EpicPandaForce Aug 19 '14 at 10:10

4 Answers4

3

There are very few situations in which a bound like class Some<E extends Some<E>> is necessary. Most of the time people write this, the bound is not actually utilized in the code, and class Some<E> would work just as well.

However, there are some particular situations in which the bound in class Some<E extends Some<E>> is actually used. For example:

abstract class Some<E extends Some<E>> {
    abstract E foo();
    Some<E> bar() {
        return foo();
    }
}

As to your question -- what about class Some<E extends Some>? Well, first most glaring issue is that you're using a raw type. Raw types should never be used in new code. But you are not convinced.

The raw type with the above class (class Some<E extends Some>) does compile with a warning (which you can ignore to your own peril). However, the raw type means it's possible to do unsafe things with it.

It takes some effort to come up with an example to demonstrate that it's unsafe. Here is one:

abstract class Some<E extends Some> {
    abstract E foo();
    Some<E> bar() {
        return foo();
    }
}

class SomeFoo extends Some<SomeFoo> {
    SomeFoo foo() { return this; }
}

class SomeBar extends Some<SomeFoo> {
    SomeFoo foo() { return new SomeFoo(); }
}

class SomeBaz extends Some<SomeBar> {
    SomeBar foo() { return new SomeBar(); }
}

// then in some method:
Some<SomeBar> a = new SomeBaz();
Some<SomeBar> b = a.bar();
SomeBar c = b.foo();

The code compiles with warnings but no errors, and throws a ClassCastException at runtime.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • This is really interesting, and indeed the `ClassCastException` is thrown at the `SomeBar c = b.foo();` line, but I can't wrap my head around why exactly it happens, can you please explain it a bit? The three subclasses confuse me. – EpicPandaForce Aug 19 '14 at 08:44
  • 2
    @Zhuinden: It happens because of an unsafe unchecked conversion in `Some.bar()`, from the result of `foo()` (of type `E`, which is a subtype of `Some`) to `Some`. The raw type `Some` can be implicitly converted to `Some`, but it is not safe, because it could be `Some`. I simply made an example where that is the case. Basically, the conversion is only safe if `E` is also a `Some`. But in `class SomeBaz extends Some`, the `E` does not satisfy this (`SomeBar` does not extend `Some`). – newacct Aug 19 '14 at 18:35
2

Concerning the statement

every time I've tried to remove additional - no new errors/warnings came up.

This should not be the case. It should print a warning, because you are using the raw type Some, and the result of this is missing type safety, as demonstrated in the answer by newacct.


The Dog/Cat example is a bit contrived, and somewhat flawed. You suggested declaring a class

public class Dog extends Animal<Cat> { } // Note "Cat" !!!

But here, the type parameter basically means: "This parameter (Cat) is the type that objects of this class (Dog) can be compared with". You are thus explicitly stating that a Dog should be comparable to Cat. Even with sophisticated languages and smart compilers, after all, it's in the responsibility of the programmers to write code that makes sense.


Indeed, there are not many cases where these self-referential generic types are necessary. One of these examples is sketched in this FAQ entry: It declares a structure of nodes (that is, a tree) where the type parameter can be used to decouple the definition of the tree structure from the actual type of the nodes:

public class RecurringTest {
    public static void main(String[] args) {
        SpecialNode sa = new SpecialNode(null);
        SpecialNode sb = new SpecialNode(sa);
        SpecialNode s = sa.getChildren().get(0);
    }
}

abstract class Node<N extends Node<N>> {
    private final List<N> children = new ArrayList<N>();
    private final N parent;

    protected Node(N parent) {
        this.parent = parent;
        if (parent != null) {
            this.parent.getChildren().add(getThis());
        }
    }

    abstract N getThis();

    public N getParent() {
        return parent;
    }

    public List<N> getChildren() {
        return children;
    }
}

class SpecialNode extends Node<SpecialNode> {
    public SpecialNode(SpecialNode parent) {
        super(parent);
    }

    SpecialNode getThis() {
        return this;
    }

}

But from my personal experience, I can say that when you think you need to create such a type, you should thoroughly think about the benefits and drawbacks. The latter mainly refers to reduced readability. When you have the choice between methods like

Node<? extends Node<? extends N, ? extends T>, T> doSomething(
    Node<? super Node<? extends N>, ? extends T> p, 
    Node<? extends Node<? super N>, ? super T> c) { ... }

that are type safe, or methods like

Node doSomething(Node parent, Node child) { ... }

that are not type safe (because of raw types, or simply because the types have not been genericified), then I'd prefer the latter. Code is read by humans.

Community
  • 1
  • 1
Marco13
  • 53,703
  • 9
  • 80
  • 159
1

The difference is that Some<E extends Some> means two things:

  • E is a raw type. In brief, raw types have all generic information stripped from the class, which in turn can create unexpected behaviour
  • E can be any class of Some!

Conversely, using Some<E extends Some<E>> means:

  • E is typed
  • E must be the same class as the class in which it's declared

The raw part an issue, but the bigger implication is the type bounds. This code demonstrates the difference, with the "B" classes using (or trying to use) the "A" classes as the generic type.

// The raw version
interface Some<E extends Some> {
    E get();
}

class SomeA implements Some<SomeA> {
    public  SomeA get() {
         return new SomeA();
    }
}

// Compiles OK
class SomeB implements Some<SomeA> {
   public SomeA get() {
         return new SomeA();
    }
}

// The typed version
interface SomeT<E extends SomeT<E>> {
    E get();
}

class SomeTA implements Some<SomeTA> {
    public  SomeTA get() {
         return new SomeTA();
    }
}

// Compile error 
class SomeTB implements SomeT<SomeA> {
   public SomeA get() { 
         return new SomeTA();
   }
}

Class SomeB compiles fine - the type of E is bound only to Some, so any Some class will do, but class SomeTB throws a compile error:

type argument SomeTA is not within bounds of type-variable E

The type of E must be exactly the same as the containing class.

This is important if you're expecting parameters and return types to be the same as the class itself, which is usually what you want with typed classes.


The type being bounded by a raw type is less of an issue, because it's still a Some, but there may be cases where it makes a difference (I can't think of any just now).

Bohemian
  • 412,405
  • 93
  • 575
  • 722
0

It looks like currently Some<E extends Some<E>> has no adventage over Some<E extends Some>

According to article written in conjuction with Java Champions and creator of Java Generics FAQ:

Article admits some possibilites/assumptions:

  • since Enum is a generic class, we should only ever use it in conjunction with a type
  • Java might become more strict and raw types could become illegal

I think that Enum would have been sufficient. However, as Philip Wadler pointed out to me, since Enum is a generic class, we should only ever use it in conjunction with a type. In future, Java might become more strict and raw types could become illegal. We therefore have the choice of writing either Enum<E extends Enum<E>> or Enum<E extends Enum<?>>, of which the first option is more accurate. Even though the compiler currently does not show me a difference, I might see warnings in future, so I follow that idiom in my class as well

Kinga Odecka
  • 387
  • 4
  • 13