The Artima Developer Community
Sponsored Link

Computing Thoughts
Self-bounding generics
by Bruce Eckel
November 11, 2005
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>>


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/

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/

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/

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?

Talk Back!


RSS Feed


About the Blogger



Sponsored Links

