The Artima Developer Community
Sponsored Link

Weblogs Forum
Self-bounding generics

28 replies on 2 pages. Most recent reply: Mar 30, 2010 6:46 AM by Ranga Phani

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 28 replies on 2 pages [ 1 2 | » ]
Bruce Eckel

Posts: 875
Nickname: beckel
Registered: Jun, 2003

Self-bounding generics (View in Weblogs)
Posted: Nov 11, 2005 1:55 PM
Reply to this message Reply
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?


Faiser _

Posts: 1
Nickname: ffaiser
Registered: Nov, 2005

Re: Self-bounding generics Posted: Nov 11, 2005 4:16 PM
Reply to this message Reply
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).

There's a good description on this page( http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.htm l)
that describes exactly this.

Jay Sachs

Posts: 30
Nickname: jaysachs
Registered: Jul, 2005

Re: Self-bounding generics Posted: Nov 13, 2005 5:44 AM
Reply to this message Reply
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):
interface HasEquals<T extends HasEquals<T>> {
  boolean equals(T o);
}

since it's reasonably considered a but to ask
  new Integer(3).equals("hello");

This pattern could apply anytime you have an operation where the arguments should "match" the target object.

Bruce Eckel

Posts: 875
Nickname: beckel
Registered: Jun, 2003

Re: Self-bounding generics Posted: Nov 13, 2005 2:00 PM
Reply to this message Reply
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?

Jay Sachs

Posts: 30
Nickname: jaysachs
Registered: Jul, 2005

Re: Self-bounding generics Posted: Nov 13, 2005 5:27 PM
Reply to this message Reply
> 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.

Bruce Eckel

Posts: 875
Nickname: beckel
Registered: Jun, 2003

Re: Self-bounding generics Posted: Nov 14, 2005 7:56 AM
Reply to this message Reply
> Hence, Comparable is
> written
>
> interface Comparable<T extends Comparable<T>> {
>   int compareTo(T o);
> }


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?

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Self-bounding generics Posted: Nov 14, 2005 8:01 AM
Reply to this message Reply
> 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.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Self-bounding generics Posted: Nov 14, 2005 8:20 AM
Reply to this message Reply
> 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:
class Foo<self> // Foo<T extends self>
{
}

Bruce Eckel

Posts: 875
Nickname: beckel
Registered: Jun, 2003

Re: Self-bounding generics Posted: Nov 14, 2005 11:29 AM
Reply to this message Reply
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;
    return this;
  }
  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); }
}
 
public class SelfBounding2 {
  public static void 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);
  }
} ///:~


Thoughts or refinements? Thanks.

Jay Sachs

Posts: 30
Nickname: jaysachs
Registered: Jul, 2005

Re: Self-bounding generics Posted: Nov 14, 2005 11:38 AM
Reply to this message Reply
> 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"?

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Self-bounding generics Posted: Nov 14, 2005 12:26 PM
Reply to this message Reply
> > 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.)
public class Test
{
    public static void main(String[] args)
    {
        A a = new B().f();
        //B b = new B().f(); does not compile
    }
}
 
abstract class 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.

Jay Sachs

Posts: 30
Nickname: jaysachs
Registered: Jul, 2005

Re: Self-bounding generics Posted: Nov 14, 2005 3:04 PM
Reply to this message Reply
> A self-bounded type is not a self-type.

Indeed, you're right.

Mohan Radhakrishnan

Posts: 7
Nickname: mindspace
Registered: Oct, 2005

Re: Self-bounding generics Posted: Nov 14, 2005 10:53 PM
Reply to this message Reply
The second part of this post http://blogs.sun.com/roller/page/ahe/Blog/20051031 deals with this issue.

Mohan Radhakrishnan

Posts: 7
Nickname: mindspace
Registered: Oct, 2005

Re: Self-bounding generics Posted: Nov 15, 2005 2:41 AM
Reply to this message Reply
public class Edible {
	public void a() {}
}
public abstract class Fruit extends Edible{
	public void a() {}
	public abstract void b();
	public abstract void c();
 
}
public class Citrus extends Fruit {
	public void a() {}
	public void b() {}
	public void c() { System.out.println("Citrus");}
 
}
public class Orange{
	public void call( Fruit f ){      --> Super
		f.b(); 
	}
}
public class Lemon extends Orange{
	public  void call( Citrus c ){    --> Sub
		c.c(); 
	}
}


Can't the above classes considered to be using argument covariance without self-bounded types?

Thanks,
Mohan

Mike Capp

Posts: 11
Nickname: mikecapp
Registered: Aug, 2005

Re: Self-bounding generics Posted: Nov 15, 2005 4:50 AM
Reply to this message Reply
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"?

Flat View: This topic has 28 replies on 2 pages [ 1  2 | » ]
Topic: Notes From the Java Posse Roundup 2010 Previous Topic   Next Topic Topic: Writing Software is Like ... Writing

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use