Summary
There's one rather mind-bending idiom that appears periodically in Java generics. Here's what it looks like: class SelfBounded<T extends SelfBounded<T>>
Advertisement
This has the dizzying effect of two mirrors pointed at each other, a kind of infinite reflection. The class SelfBounded takes a generic argument T, T is constrained by a bound, and that bound is this class, with T as an argument.
Although it's difficult to parse when you first see it, it emphasizes that the extends keyword, when used with bounds, is definitely different than when it is used to create classes.
To understand what this idiom means, look at how the resulting class can and can't be used:
//: generics/SelfBounding.java
class SelfBounded<T extends SelfBounded<T>> {
T element;
SelfBounded<T> set(T arg) {
element = arg;
return this;
}
T get() { return element; }
}
class A extends SelfBounded<A> {}
class B extends SelfBounded<A> {} // Also OK
class C extends SelfBounded<C> {
C setAndGet(C arg) { set(arg); return get(); }
}
class D {}
// Can't do this:
// class E extends SelfBounded<D> {}
// Compile error: Type parameter D is not within its bound
public class SelfBounding {
public static void main(String[] args) {
A a = new A();
a.set(new A());
a = a.set(new A()).get();
a = a.get();
C c = new C();
c = c.setAndGet(new C());
}
} ///:~
What self-bounding does is require the use of the class in an inheritance relationship like this:
class A extends SelfBounded<A> {}
This forces you to pass the class that you are defining as a parameter to the base class.
The type parameter must be the same as the class being defined. As you can see in the definition of class B, you can also derived from a SelfBounded that uses a parameter of another SelfBounded, although the predominant use seems to be the one that you see for class A. The attempt to define E shows that you cannot use a type parameter that is not a SelfBounded.
Notice that you can remove the constraint and all the classes will still compile, but E will also compile:
//: generics/NotSelfBounded.java
public class NotSelfBounded<T> {
T element;
NotSelfBounded<T> set(T arg) {
element = arg;
return this;
}
T get() { return element; }
}
class A2 extends NotSelfBounded<A2> {}
class B2 extends NotSelfBounded<A2> {}
class C2 extends NotSelfBounded<C2> {
C2 setAndGet(C2 arg) { set(arg); return get(); }
}
class D2 {}
// Now this is OK:
class E2 extends NotSelfBounded<D2> {} ///:~
So clearly, the self-bounding constraint serves only to force the inheritance relationship. If you use self-bounding, you know that the type parameter used by the class will be the same basic type as the class that's using that parameter. It forces anyone using that class to follow that form.
It's also possible to use self-bounding for generic methods:
//: generics/SelfBoundingMethods.java
public class SelfBoundingMethods {
static <T extends SelfBounded<T>> T f(T arg) {
return arg.set(arg).get();
}
public static void main(String[] args) {
A a = f(new A());
}
} ///:~
So the question is: What does this constraint buy you?
One of the nice things it offers is that you can define methods within the class that use parameters of the same type as this new class.
Using your example:
class SelfBounded<T extends SelfBounded<T>> { T element; SelfBounded<T> set(T arg) { element = arg; return this; } T get() { return element; } }
class A extends SelfBounded<A> {}
....and....
A a = new A(); a.set(new A());
........... It's not obvious here, but in a.set(T), class A is guaranteed to receive an argument of type A. This is nice for methods such as compareTo(T) (where it makes sense to define the method as necessarily operating on arguments of the same type of its enclosing class).
The self-bounded generic is a way of expressing a MyType relationship for parameters in subclasses. This is an instance of covariant parameter types, which normally is prohibited (or is turned into ad-hoc overloading). It provides a way to actually enforce something like "As this class is subclassed, the argument to method f must follow that subclassing".
As the previous poster suggested, a canonical instance of this is the Comparable interface. Without the self-bounded generic, one could only write
interface Comparable {
int compareTo(Object o);
}
which would allow compareTo() to be applied to any argument at all. It'd be nice if the compiler ensured we only compared "similar" objects. Hence, Comparable is written
interface Comparable<T extends Comparable<T>> {
int compareTo(T o);
}
A similar case could be made for equality (though Java doesn't do this):
So Jay, if I understand you correctly you're saying that self-bounding generics are about argument types rather than return types? In particular, allowing the argument types to vary to follow subclassing?
> So Jay, if I understand you correctly you're saying that > self-bounding generics are about argument types > rather than return types? In particular, allowing the > argument types to vary to follow subclassing?
That's where their value lies, yes. Since return types are permitted to vary covariantly during subclassing, self-bounded generics gain you no additional expressiveness there. It's the ability to specify "this argument should be 'just like me', even after subclassing", that is only expressible by self-bounded generics.
> Because it doesn't actually appear that way in the JDK > (see the Javadocs); it's just > > interface Comparable<T> { > int compareTo(T o); > } > > I assume that you're saying here that it could be > written that way?
I believe it cannot be done because it would cause too many problems with existing code.
> That's where their value lies, yes. Since return types are > permitted to vary covariantly during subclassing, > self-bounded generics gain you no additional > expressiveness there. It's the ability to specify "this > argument should be 'just like me', even after > subclassing", that is only expressible by self-bounded > generics.
You probably understand this but let's be absolutely clear: if by 'just like me' you mean 'same type as this' that last sentence is false. A self-bounded type is not a self-type. It's a common misconception that self-bounded types are equivalent to self-types. See the "Generics and covariant return types" post# 8 for example of this.
A self-bounded type cannot guarantee that the argument will be the same type as this after subclassing. There is no equivalent to self-types in Java generics. Personally, I think the syntax should be added to allow something like:
OK, to sum it up: self-bounding types are to enable covariant argument types.
Here's my current example to show this. However, as discussed, returning this produces a kind of weird type. I got it to work, but I have no idea if it's useful or not:
//: generics/SelfBounding2.java
class SelfBounded2<T extends SelfBounded2<T>> {
T element;
SelfBounded2<T> set(T arg) {
element = arg;
returnthis;
}
T get() { return element; }
void f(T arg) {}
void g(T arg) { element.f(arg); }
}
class User extends SelfBounded2<User> {
void h(User arg) { g(arg); }
}
publicclass SelfBounding2 {
publicstaticvoid main(String[] args) {
User user1 = new User(), user2 = new User();
SelfBounded2<User> sbUser = user1.set(user2);
User user4 = user2.get();
user1.f(user2);
user2.g(user1);
user1.h(user2);
}
} ///:~
> You probably understand this but let's be absolutely > clear: if by 'just like me' you mean 'same type as > this' that last sentence is false. A self-bounded > type is not a self-type. It's a common > misconception that self-bounded types are equivalent to > self-types. See the "Generics and covariant return types" > post# 8 for example of this. > A self-bounded type cannot guarantee that the argument > will be the same type as this after subclassing.
Hmm. If you mean "exactly the same type", I agree. The self-generic bound type will be a subtype of the type of 'this', though. I looked at the post you described and didn't see this contradicted.
What you're referring to here:
> There is no equivalent to self-types in Java generics.
are you referrring to "exact types"? and where MyType generally implies "exact MyType"?
> > You probably understand this but let's be absolutely > > clear: if by 'just like me' you mean 'same type as > > this' that last sentence is false. A > self-bounded > > type is not a self-type. It's a common > > misconception that self-bounded types are equivalent to > > self-types. See the "Generics and covariant return > types" > > post# 8 for example of this. > > A self-bounded type cannot guarantee that the argument > > will be the same type as this after subclassing. > > Hmm. If you mean "exactly the same type", I agree. The > self-generic bound type will be a subtype of the type of > 'this', though. I looked at the post you described and > didn't see this contradicted.
I think you misunderstand. The post I referred to is an example of this misconception.
I'll just post the counter-example here (Bruce already gave it in in the blog, actually.)
publicclass Test
{
publicstaticvoid main(String[] args)
{
A a = new B().f();
//B b = new B().f(); does not compile
}
}
abstractclass HasF< T extends HasF< T > > {
@SuppressWarnings( "unchecked" )
T f() {
System.out.println( "HasF.f()" );
return (T)this;
}
}
class A extends HasF<A>
{
}
class B extends HasF<A>
{
}
If you run the above, you will get a ClassCastException. This is because T is not guaranteed to be an instance of this.getClass(). A self-bounded type is not a self-type.
I don't really speak Java and so may be missing some of the constraint semantics here, but isn't this just C++'s "Curiously Recurring Template Pattern"?
Not really. He skips over the self-bounded type or F-bound as he calls it and moves to a method declaration. The problem isn't really there with method declarations.
> A way to ensure using the same type as parameter > for compareTo() would be the self-bounded > type, as stated by James: > > interface Comparable<self> { > int compareTo(self o); > }
The good news is that you can get the desired result in other places e.g.
class ReverseComparator<T extends Comparable<T>> implements Comparator<T>
{
publicint compare(T a, T b)
{
return -a.compareTo(b);
}
}
abstractclass HasF< T extends HasF< T > > {
T f() {
System.out.println( "HasF.f()" );
return (T)this;
}
}
class A extends HasF< A > {}
class B extends HasF< A > {}
...
A a = new B().f();
Is an example of the type system not getting a type error, the line A a = new B().f(); fails with a ClassCastException when it tries to assign an object of type B to a which is of type A. The type checking was effectively bypassed by the cast in the line return (t)this; and the compiler issues a warning about the unsafe cast.
I don't think this is a particularly serious problem since:
1. Even the most rudimentry of testing will get the problem. The line only needs to be executed, its not even value dependent.
2. The alternate line HasF< A > a = new B().f(); is OK
3. Also the problem only arises when this is returned. Consider:
abstractclass HasF< T extends HasF< T > > {
T f() {
System.out.println( "HasF.f()" );
return newInstance();
}
abstract T newInstance();
}
class A extends HasF< A > {
A newInstance() { returnnew A(); } // OK
}
class B extends HasF< A > {
B newInstance() { returnnew B(); } // Error flagged
}
No problems with now, the compiler gets the error.
A further example in this forum is Comparable. The example in the forum did not show method bodies. But consider the example with even the most trivial method body:
// java.lang.Comparable needs to erase to Object, not Comparable
// hence it isn't a recursive generic because that erases to Comparable
interface Comparable2< T extends Comparable< T > > {
int compareTo( T other );
}
class C implements Comparable2< C > {
int v;
C( int v ) { this.v = v; }
publicint compareTo( C other ) { return v - other.v; }
}
class D implements Comparable2< C > {
float v;
D( float v ) { this.v = v; }
// does not compile - other.v has a different type and
// probably in practice different visibility than v
publicint compareTo( C other ) { return v - other.v; } }
Again no problem in practice, the compiler gets the error. (You are highly unlikely to have two unrelated methods with exactly the right fields that are visable in both classes.)
Also there is no problem with a compareTo method in a derrived class since you would almost certainly make it final.
Although self types have some advantages over recursive generic types, the question is: is it worth adding them? Does anyone have any better examples of where they are needed.
If they are added, my favoutite syntax is:
class HasF {
this f() {
...
returnthis;
}
int compareTo( this other ) { ... }
}
Because this syntax does not need any new keywords and emphasises that when used as a return type it is this that you are returning.
> Someone asked this question already. Why is this not the > 'Curiously Recurring Template Pattern' ?
It is. But without the bound you can apply the generic to any type in the base class. With the bound, you can only apply the base-class template to the class you're deriving.
And CRTP as shown in C++ has different properties and applications, because C++ templates are more powerful (they have duck typing and don't use erasure), so you can't just map CRTP to self-bounding types.
defines a fix point. Different X's will lead to different solutions for Y. This is a bit like solving x=f(x) with f=tan, sin, exp, etc but now for a class/interface. It is a recursive definition and is the only way to provide certain generics constructs mathematically correctly. So the question is not what the constraint brings but how to define a class correctly.
If you want to delve deeper into type theory, see the articles on the subject at http://www.jot.fm/issues/issue_2002_09/column4, esp. see how iteration leads to a correct solution for each case:)
If we consider this pattern as a kind of variant of CRTP, method inheritance gets important. However, the code you've shown is actually implementing something like 'newInstance ()'. What's more important: compareTo() method is overrided rather than inherited.
For example,
abstractclass HasF< T extends HasF< T > > {
T f() {
System.out.println( "HasF.f()" );
return newInstance();
}
abstract T newInstance();
}
class A extends HasF< A > {
A newInstance() { returnnew A(); } // OK
}
class B extends HasF< A > {
// We have to code this method to raise an compiler error.
B newInstance() { returnnew B(); }
}
If that implementations are needed, is this pattern still useful?
Interestingly, these declarations have the effect that a class cannot declare itself to only implement just one of A or B, but must implement both (or neither).
I leave it as an exercise for the reader to find a use for this construct.
> > Because it doesn't actually appear that way in the JDK > > (see the Javadocs); it's just > > > > interface Comparable<T> { > > int compareTo(T o); > > } > > > > I assume that you're saying here that it could > be > > written that way? > > I believe it cannot be done because it would cause too > many problems with existing code.
It's also probably not done becuase it's overly-restrictive. Theoretically, if Java permitted multiple occurrences of a generic interface with different type arguments, I could see it being useful to declare
class C implements Comparable<Integer>, Comparable<String> { ... }
In the literature where people try and show that other forms of generics are better than generics with type arguments an often used example is the observer/subscriber pattern. In Java because mutual recursion is allowed this pattern can be accomplished with out too much fuss:
import java.util.*;
abstractclass Subject< S extends Subject< S, O >, O extends Observer< S, O > > {
privatefinal List< O > observers = new ArrayList< O >();
void subscribe( O obs ) { observers.add( obs ); }
void publish() {
for ( final O obs : observers )
obs.notify( (S)this );
}
}
abstractclass Observer< S extends Subject< S, O >, O extends Observer< S, O > > {
abstractvoid notify( S sub );
}
class Sensor extends Subject< Sensor, Display > {
double value = 0.0;
void changeValue( finaldouble v ) {
value = v;
publish();
}
}
class Display extends Observer< Sensor, Display > {
void notify( Sensor sub ) {
System.out.println( sub + " has value " + sub.value );
}
}
publicclass SubjectObserver {
publicstaticvoid main( final String[] notUsed ) {
final Display o = new Display();
final Sensor s = new Sensor();
s.subscribe( o );
s.changeValue( 1 );
}
}
Other types of generics like virtual types in Beta do this pattern better than Java style generics, but virtual types are worse at things like collections. So I guess the Java people decided their form of generics is on balance the best choice. Scala does both forms of generics, they are not mutually exclusive, but obvously that is more stuff to learn.
....so perhaps a restatement of the crux of this issue is warranted. Is it fair to say that there are two flavors of "selftype". One is "absolute", in that it refers to a specific class type, independent of inheritance. The other is "relative", in that it refers to whatever "this" class happens to be, and cannot be obtained via generics (and the generic self-type pattern doesn't cut it).
For example, using THIS as the hypothetical relative generic selftype....
myObj.setMyType(myObj); // OK myObj.setMyType(mySubObj); // OK myObj.setThis(myObj); // OK, myObj is a MyType myObj.setThis(mySubObj); // OK, mySubObj is a MyType
mySubObj.setMyType(myObj); // OK mySubObj.setMyType(mySubObj); // OK, mySubObj is a MyType mySubObj.setMySubType(myObj); // NO, myObj not a MySubType mySubObj.setMySubType(mySubObj); // OK mySubObj.setThis(myObj) // NO, myObj not a MySubType mySubObj.setThis(mySubObj); // OK, mySubObj is a MySubType