Sponsored Link •
|
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?
Have an opinion? Readers have already posted 28 comments about this weblog entry. Why not add yours?
If you'd like to be notified whenever Bruce Eckel adds a new entry to his weblog, subscribe to his RSS feed.
Bruce Eckel (www.BruceEckel.com) provides development assistance in Python with user interfaces in Flex. He is the author of Thinking in Java (Prentice-Hall, 1998, 2nd Edition, 2000, 3rd Edition, 2003, 4th Edition, 2005), the Hands-On Java Seminar CD ROM (available on the Web site), Thinking in C++ (PH 1995; 2nd edition 2000, Volume 2 with Chuck Allison, 2003), C++ Inside & Out (Osborne/McGraw-Hill 1993), among others. He's given hundreds of presentations throughout the world, published over 150 articles in numerous magazines, was a founding member of the ANSI/ISO C++ committee and speaks regularly at conferences. |
Sponsored Links
|