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 ]
James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Self-bounding generics Posted: Nov 15, 2005 6:35 AM
Reply to this message Reply
Advertisement
> The second part of this post
> http://blogs.sun.com/roller/page/ahe/Blog/20051031 deals
> with this issue.

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.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Self-bounding generics Posted: Nov 15, 2005 6:42 AM
Reply to this message Reply
> Can't the above classes considered to be using argument
> covariance without self-bounded types?

Normally that's called 'method overloading'. The call(Citrus) method defined in Lemon isn't related to the call(Fruit) method defined in Orange.

try this:

Citrus citrus = new Citrus();
Lemon lemon = new Lemon();
 
lemon.call(citrus);
lemon.call((Fruit) citrus);
((Orange) lemon).call(citrus);

Adrian Barna

Posts: 1
Nickname: adibarna
Registered: Nov, 2005

Re: Self-bounding generics Posted: Nov 15, 2005 7:58 AM
Reply to this message Reply
Jay, interface definition

interface Comparable<T extends Comparable<T>> {  // (1)
  int compareTo(T o);
}
isn't much better than

interface Comparable2<T> {  // (2)
  int compareTo(T o);
}
One can misuse (2) in one step:

class A {}
 
class B implements Comparable2<A> {
  int compareTo(A o) {...}
}
and can also misuse (1) in two steps:

class C implements Comparable<C> {
  int compareTo(C o) {...}
}
 
class D implements Comparable<C> {
  int compareTo(C o) {...}
}
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);
}

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Self-bounding generics Posted: Nov 15, 2005 12:32 PM
Reply to this message Reply
> 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>
{
   public int compare(T a, T b)
   {
       return -a.compareTo(b);
   }
}

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Self-bounding generics Posted: Nov 15, 2005 7:44 PM
Reply to this message Reply
The example given in this and other forums:

abstract class 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:
abstract class HasF< T extends HasF< T > > {
    T f() {
        System.out.println( "HasF.f()" );
        return newInstance();
    }
    abstract T newInstance();
}
 
class A extends HasF< A > {
    A newInstance() { return new A(); } // OK
}
 
class B extends HasF< A > {
    B newInstance() { return new 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; }
    public int 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
    public int 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() {
        ...
        return this;
    }
    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.

Mohan Radhakrishnan

Posts: 7
Nickname: mindspace
Registered: Oct, 2005

Re: Self-bounding generics Posted: Nov 16, 2005 3:25 AM
Reply to this message Reply
Someone asked this question already. Why is this not the 'Curiously Recurring Template Pattern' ?
public class PropertySupportAspect<T extends PropertySupportAspect<T>> {
 
   PropertyChangeSupport support = new 
                           PropertyChangeSupport(this);
 
   public void addPropertyChangeListener
       ( PropertyChangeListener listener){
     support.addPropertyChangeListener(listener);
   }
 
   public void addPropertyChangeListener(
                   String propertyName,
		   PropertyChangeListener listener)  {
     support.addPropertyChangeListener(propertyName, 
                                       listener);
   }
 
   public void removePropertyChangeListener(
                   String propertyName,
	           PropertyChangeListener listener) {
     support.removePropertyChangeListener(propertyName, 
                                          listener);
    }
 
    public void removePropertyChangeListener
                     (PropertyChangeListener listener) {
     support.removePropertyChangeListener(listener);
    }
 
    public void hasListeners(String propertyName) {
     support.hasListeners(propertyName);
    }
 
    public void firePropertyChange( Bean b,
				    String property,
				    String oldval,
				    String newval) {
     support.firePropertyChange( property,
			    ( oldval == null ) ?
			  	  oldval :
			  	  new String(oldval),
	   	                  new String(newval));
    }
}
class Bean extends PropertySupportAspect<Bean>{
	private String name;
 
	public String getName() {
		return name;
	}
	public void setName( String name ) {
		firePropertyChange( this,
		                    "name",
				    getName(),
				    name );
		this.name = name;
	}
}

Bruce Eckel

Posts: 875
Nickname: beckel
Registered: Jun, 2003

Re: Self-bounding generics Posted: Nov 16, 2005 3:43 AM
Reply to this message Reply
> 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.

Werner Schulz

Posts: 18
Nickname: werner
Registered: May, 2005

Re: Self-bounding generics Posted: Nov 16, 2005 4:35 PM
Reply to this message Reply
The generics notation
class Y implements/extends  X<Y>
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:)

Minkoo Seo

Posts: 3
Nickname: minkoo
Registered: Nov, 2005

Re: Self-bounding generics Posted: Nov 23, 2005 9:23 AM
Reply to this message Reply
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,

 abstract class HasF< T extends HasF< T > > {
     T f() {
         System.out.println( "HasF.f()" );
         return newInstance();
     }
     abstract T newInstance();
 }
  
 class A extends HasF< A > {
     A newInstance() { return new A(); } // OK
 }
  
 class B extends HasF< A > {
     // We have to code this method to raise an compiler error.
     B newInstance() { return new B(); }
 }


If that implementations are needed, is this pattern still
useful?

Jay Sachs

Posts: 30
Nickname: jaysachs
Registered: Jul, 2005

Mutually Self-bounding generics Posted: Dec 2, 2005 12:53 PM
Reply to this message Reply
My first attempt at mutual recursion (correctly) wouldn't compile:

interface A<T extends B<T>> {
T f(T x);
}
interface B<U extends A<U>> {
U g(U y);
}

A bit of thought led me to realize how to get it to compile:

interface A<T extends A<T> & B<T>> {
T f(T x);
}
interface B<U extends A<U> & B<U>> {
U g(U y);
}

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.

Jay Sachs

Posts: 30
Nickname: jaysachs
Registered: Jul, 2005

Re: Self-bounding generics Posted: Dec 5, 2005 6:37 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.

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> { ... }

but of course this is disallowed (erasure).

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Mutually Self-bounding generics Posted: Dec 6, 2005 10:53 PM
Reply to this message Reply
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.*;
 
abstract class Subject< S extends Subject< S, O >, O extends Observer< S, O > > {
    private final List< O > observers = new ArrayList< O >();
    void subscribe( O obs ) { observers.add( obs ); }
    void publish() {
        for ( final O obs : observers )
            obs.notify( (S)this );
    }
}
abstract class Observer< S extends Subject< S, O >, O extends Observer< S, O > > {
    abstract void notify( S sub );
}
class Sensor extends Subject< Sensor, Display > {
    double value = 0.0;
    void changeValue( final double v ) {
        value = v;
        publish();
    }
}
class Display extends Observer< Sensor, Display > {
    void notify( Sensor sub ) {
        System.out.println( sub  + " has value " + sub.value );
    }
}
public class SubjectObserver {
    public static void 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.

Jon Barrilleaux

Posts: 1
Nickname: jonbarril
Registered: Mar, 2007

Re: Self-bounding generics Posted: Mar 23, 2007 2:48 PM
Reply to this message Reply
> A self-bounded type is not a self-type.


Self-type is near and dear to my heart....


http://java.net/cs/user/view/cs_msg/37432


....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....


interface MyType {
void setMyType(MyType absoluteSelfTypeObj);
void setThis(THIS relativeSelfTypeObj);
}

interface MySubType extends MyType {
void setMySubType(MySubType absoluteSelfTypeObj);
}

MyType myObj;
MySubType mySubObj;


Then.....


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

Ranga Phani

Posts: 1
Nickname: phaniranga
Registered: Mar, 2010

Re: Self-bounding generics Posted: Mar 30, 2010 6:46 AM
Reply to this message Reply
Can u please make me understand the problem with this code ....

public class TestEntry<T extends TestEntry<T,P,PK>,P extends TestEntry<P,? extends TestEntry<?, ?, PK> ,PK>, PK extends Serializable & Comparable<PK>>{

}

This gives an error .... type parameter P is not within its bound ....

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