The Artima Developer Community
Sponsored Link

Weblogs Forum
Use cases for Generics

39 replies on 3 pages. Most recent reply: Jan 12, 2006 10:59 AM by Mo Welch

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 39 replies on 3 pages [ « | 1 2 3 | » ]
Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Use cases for Generics Posted: Oct 30, 2005 5:02 PM
Reply to this message Reply
Advertisement
@Vladimir Nesov

This is another good use of generics, the recursive pattern. Your example is an example of mutual recursion; there is also self recursion, e.g.:
class Enum< E extends Enum< E > >

and if backwards compatability wasn't a problem:
interface Comparable< T extends Comparable< T > >

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Use cases for Generics Posted: Oct 31, 2005 7:16 AM
Reply to this message Reply
#2 does not require generics and therefore is not a good use case for generics IMO. Covariant return types handle this case.

The following compiles without error or warning:
// 2. Return a specific type, rather than a base type:
 
interface MyInterface2 {
  Object returnMySpecificType();
  // ...
}
 
class Pet {}
class Dog extends Pet {}
class Waffle {}
 
class MI2Test1 implements MyInterface2 {
  public Pet returnMySpecificType() { return new Pet(); }
}
 
class MI2Test2 implements MyInterface2 {
  public Dog returnMySpecificType() { return new Dog(); }
}
 
class MI2Test3 implements MyInterface2 {
  public Waffle returnMySpecificType() {
    return new Waffle();
  }
}

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Use cases for Generics Posted: Oct 31, 2005 7:19 AM
Reply to this message Reply
Likewise 3 is handled by covariant types with the following declaration of MyInterface3

interface MyInterface3 {
  Pet returnMySpecificType();
  // ...
}

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Use cases for Generics Posted: Oct 31, 2005 7:38 AM
Reply to this message Reply
> Not exactly. This is one of the things that get tricky.
> Polymorphism is still working here. The issue that
> covariance (List<? extends something>) deals with is the
> "type of the container." A List-of-Shape, AS A CONTAINER,
> has a completely different type than a List-of-Circle as
> a container. That's the problem.

I'm pretty sure you are mixing-up the terminology here. 'Covariance' (or more precisely covariant return types) in Java refers to the added feature that a subclass can override a method and make the return type a more specific type. e.g.

interface A
{
   Number foo();
}
 
interface B extends A
{
   Integer foo();
}


Variance in Java generics refers to the way generic types can interact with each other:

List<Object> list = new List<String> // nope, not allowed
 
List<? extends Object> = new List<String> // OK

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Use cases for Generics Posted: Oct 31, 2005 8:05 AM
Reply to this message Reply
One thing that, if I were you, I would add to this would be a discussion of the use of self-types (e.g. Enum) and how they have holes in them:

public class Test<T extends Test<T>>
{
    public static void main(String[] args)
    {
        new A().foo(new B());
        new B().foo(new A());
    }
 
    public void foo(T other)
    {
    }   
}
 
class A extends Test<B>
{
}
 
class B extends Test<B>
{
}

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Use cases for Generics Posted: Oct 31, 2005 12:35 PM
Reply to this message Reply
@James Watson

I agree that with only one method in the interface example 2 isn't that strong. There is some point though, if you program to the interface rather than to the classes then the generics can avoid a cast. For example using your non-generic form of the example the following is an error:
MyInterface2 t1 = new MI2Test1();
Pet p1 = t1.returnMySpecificType(); // error - needs a cast

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Use cases for Generics Posted: Oct 31, 2005 12:46 PM
Reply to this message Reply
@James Watson

My version 5 compiler catches the error you are talking about:

C:\Personal\Java\examples\annotations\show\src\show\Main.java:8: foo(show.B) in show.Main<show.B> cannot be applied to (show.A)
new B().foo(new A());
1 error

Note I changed the class name Test to Main.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Use cases for Generics Posted: Oct 31, 2005 2:01 PM
Reply to this message Reply
> @James Watson
>
> I agree that with only one method in the interface example
> 2 isn't that strong. There is some point though, if you
> program to the interface rather than to the classes then
> the generics can avoid a cast. For example using your
> non-generic form of the example the following is an
> error:
>
> MyInterface2 t1 = new MI2Test1();
> Pet p1 = t1.returnMySpecificType(); // error - needs a
> cast

As your example is written, it gives the same error with the generic version. Of course, one can define parameter type of the t1 reference but you are getting into the real use of generics at that point.

All I am saying is that generics are not needed to return more specific types from methods. There are lots of other uses for generics, this just isn't a poweful use of them.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Use cases for Generics Posted: Oct 31, 2005 2:16 PM
Reply to this message Reply
> @James Watson
>
> My version 5 compiler catches the error you are talking
> about:
>
> C:\Personal\Java\examples\annotations\show\src\show\Main.ja
> va:8: foo(show.B) in show.Main<show.B> cannot be applied
> to (show.A)
> new B().foo(new A());
> 1 error
>
> Note I changed the class name Test to Main.

I'm not sure what you mean to show but the compiler error occurs in my compiler too. The point is that you cannot assume that when you define a 'self-type' that it will be implemented as class A implements Self<A>. Generally that's what you want to do. The issue with not doing so is that you can create non symetrical relationships as shown in my example.

Another example in a non self-type is Comparable. I can easily create the following relationship:

public class Test
{
    public static void main(String[] args)
    {
        B b = new B();
        C c = new C();
 
        b.compareTo(c);
        c.compareTo(b);
    }
}
 
class A<T extends A> implements Comparable<T>
{
   public int compareTo(T a)
   {
       return 0;
   }
}
 
class B extends A<A>{}
 
class C extends A<C>{}


i.e. Bs are comparable to Cs but cs are not Comparable to Bs. This is obviously possible in 'old' Java but generics add a layer of complexity and may add some false security.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Use cases for Generics Posted: Oct 31, 2005 5:11 PM
Reply to this message Reply
@James

> All I am saying is that generics are not needed to return
> more specific types from methods. There are lots of other
> uses for generics, this just isn't a poweful use of them.

But Bruces example is a valid example, it is essentially the same as List< E >. Both Bruce's example and List could be written without generics and use covarient return types instead, but by generifting the interface it is easier to use.

Perhaps if Bruce expanded the example and contrasted the generic with the non-generic version you would be happier with his code.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Use cases for Generics Posted: Oct 31, 2005 5:48 PM
Reply to this message Reply
@James

> i.e. Bs are comparable to Cs but cs are not Comparable to
> Bs. This is obviously possible in 'old' Java but generics
> add a layer of complexity and may add some false security.

To me the type system is doing exactly the right thing; your examples are asymmetric with respect to the types, so some examples work and not others. But if you change the code to be symmetric with respect to the types then you get symmetric results.

Currently you have:
class B extends A< A >{}
 
class C extends A< C >{}

Hence the asymmetry because B extends A< A > wheras C extends A< C >, not A< A >. Hence b.compareTo( c ) is OK but not c.compareTo( b ). As I would expect. If you want symmetry then make the declarations symmetrical, i.e.:
class B extends A< A >{}
 
class C extends A< A >{}

Then both cases will work. You have said "Generally that's what you want to do" when refering to this symmetry. So perhaps if that is the behaviour you wanted for A you could code A, B, and C as:
class A< T extends A > implements Comparable< A > {
   public final int compareTo( final A notUsed ) { return 0; }
}
 
class B extends A< B >{}
 
class C extends A< C >{}

This way you ensure that anything derived from A is comparable to any other type of A. Note that A implements Comparable< A >, not Comparable< T >.

Jody Garnett

Posts: 1
Nickname: jive
Registered: Nov, 2005

Type safe IAdaptable Posted: Nov 1, 2005 2:18 PM
Reply to this message Reply
The eclipse code base has an implementation of the Extensible interface pattern (IAdaptable, IResource). Given this exposure I am starting to see the approach used, and combined with generics.
interface IMorph {
   <T> morph( Class<T> type );
}

The trick being that this is combined with some kind of plugin system allowing the "handles" to be repurposed as required in the future.

It is worth pointing out the use of the cast method as a way to avoid compiler warnings.
public <T> morph( Class<T> type ){
   if( type.isAssignableFrom( this.getClass() ) ){
      return type.cast( this );
   }
   else {
       // engage the plugin system ...
   }
}

Cheers.

Vladimir Nesov

Posts: 27
Nickname: robotact
Registered: Aug, 2005

Re: Use cases for Generics Posted: Nov 1, 2005 3:59 PM
Reply to this message Reply
Absence of <? T> thing is indeed unnecessary limitation. There is no way to create local variable of cyclically defined type, there is one only for parameter of method and classes. Say, given definitions of my example, I can create generic method with definition
    public static <V extends Vertex<V,G>, G extends Graph<V,G>> List<V> searchOrder(Graph<V,G> graph){...}

but I can't create variable of that type G outside that method, so I'm bound to using lower type (FlowGraph) after creating object. But it's impossible if I don't want to public lower type FlowVertex, as just Vertex won't do, it should be Vertex parameterized by FlowGraph parameterazed by Vertex, parameterized by FlowGraph, ...
And I definately don't want to write all not-that-generic code concerning only concrete FlowGraph in parameterized functions.

Bruce Eckel

Posts: 875
Nickname: beckel
Registered: Jun, 2003

Re: Use cases for Generics Posted: Nov 2, 2005 4:21 AM
Reply to this message Reply
> I'm pretty sure you are mixing-up the terminology here.
> 'Covariance' (or more precisely covariant return types)
> ) in Java refers to the added feature that a subclass can
> override a method and make the return type a more specific
> type. e.g.

I should have been more precise. "Covariant return types" are as you describe them. However, the term covariant applies to a broader range of issues. For example, here:
http://www.developertutorials.com/tutorials/java/java-theory-generic-050323/page2.html
Brian Goetz explains how arrays in Java are covariant, whereas generics are not (without using wildcards).

Brian Slesinsky

Posts: 43
Nickname: skybrian
Registered: Sep, 2003

Re: Use cases for Generics Posted: Nov 4, 2005 12:09 PM
Reply to this message Reply
I'm not sure how useful this is, but it might be good to remember that you can also use generic types simply for type-checking. Example:

class Heart {}
class Club {}
class Diamond {}
class Spade {}

class Card<T> {}

void example() {
Card<Heart> a = new Card<Heart>();
Card<Heart> b;

b = a; // assignment ok

Card<Spade> c = new Card<Spade>();

b = c; // compile error

}

Here, a Card<Heart> and a Card<Spade> have the same operations, and yet you can't mix them.

Of course, this could more straightforwardly be done using four subclasses:

class HeartCard extends Card {}
class SpadeCard extends Card {}
class DiamondCard extends Card {}
class ClubCard extends Card {}

The only case where I can see it making sense is if you have multiple parameters and get a combinatorial explosion where you don't want to actually define all the subclasses.

Flat View: This topic has 39 replies on 3 pages [ « | 1  2  3 | » ]
Topic: Error in  "Building More Flexible Types with Mixins" Previous Topic   Next Topic Topic: Eureka: Forget Interfaces what I really need are Traits!

Sponsored Links



Google
  Web Artima.com   

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