The Artima Developer Community
Sponsored Link

Computing Thoughts
Use cases for Generics
by Bruce Eckel
October 26, 2005
Summary
I need to use the collective-consciousness of the Artima community, like genetic algorithms, to come up with a set of canonical use cases for generics.

Advertisement

Trying to pick them apart in previous weblogs and in the chapter, I finally realized that each example of generics that you see is usually comprised of multiple use cases, which can make them confusing to learn from. So I've started a list of use cases, where I try to focus on only one issue of generics for each item in the list.

What I'm hoping you will do is to look over this list and see if there are any (A) improvements and (B) additions that you can make. The resulting list will not only ensure that I cover everything in the chapter, but I think it will make a very nice summary of generic issues.

Thanks for your help.


import java.util.*;

// 1. Just want to call a specific method inside your 
// code. (You don't need generics for this; just pass
// the class or interface directly).

interface MyInterface1 { void method(); }

class CallMethod {
  void call(MyInterface1 mi) { mi.method(); }
}

// 2. Return a specific type, rather than a base type:

interface MyInterface2<T> {
  T returnMySpecificType();
  // ...
}

class Pet {}
class Dog extends Pet {}
class Waffle {}

class MI2Test1 implements MyInterface2<Pet> {
  public Pet returnMySpecificType() { return new Pet(); }
}

class MI2Test2 implements MyInterface2<Dog> {
  public Dog returnMySpecificType() { return new Dog(); }
}

class MI2Test3 implements MyInterface2<Waffle> {
  public Waffle returnMySpecificType() {
    return new Waffle();
  }
}

// 3. Constrain the use of (2):

interface MyInterface3<T extends Pet> {
  T returnMySpecificType();
  // ...
}

class MI3Test1 implements MyInterface3<Pet> {
  public Pet returnMySpecificType() { return new Pet(); }
}

class MI3Test2 implements MyInterface3<Dog> {
  public Dog returnMySpecificType() { return new Dog(); }
}

// Error: type parameter Waffle is not within its bound:
/*! 
class MI3Test3 implements MyInterface3<Waffle> {
  public Waffle returnMySpecificType() {
    return new Waffle();
  }
} */

// 4. Pass a generic class into a method:

class GenericUseCases4 {
  static <T> T method(List<T> list, T item) {
    // Modify the list:
    list.remove(1);
    list.clear();
    // Can call a method that takes a T argument:
    list.add(item);
    // Can call a method that returns a T argument:
    return list.get(0);
  }
  static void test() {
    method(new ArrayList<Integer>(), 1);
  }
}

// 5. Constrained form of the above:

class GenericUseCases5<U> {
  <T extends U> T method(List<T> list, T item) {
    // Modify the list:
    list.remove(1);
    list.clear();
    // Can call a method that takes a T argument:
    list.add(item);
    // Can call a method that returns a T argument:
    return list.get(0);
  }
  static void test() {
    new GenericUseCases5<Number>().method(
      new ArrayList<Integer>(), 1);
  }
}

// 6. Pass a generic class or a class holding a subtype:

class GenericUseCases6 {
  <T> T method(List<? extends T> list, T item) {
    // Modify the list:
    list.remove(1); // (Method with a non-T argument)
    list.clear();
    // Cannot call a method that takes a T argument:
    //! list.add(item);
    // Error message: cannot find symbol  : method add(T)
    // Can call a method that returns a T argument:
    return list.get(0);
  }
}

// 7. Loosen constraints but still write to the list:

class GenericUseCases7 {
  <T> void method(List<? super T> list, T item) {
    // Modify the list:
    list.remove(1);
    list.clear();
    // Can call a method that takes a T argument:
    list.add(item);
    // Requires an unsafe cast for a T return value:
    T x = (T)list.get(0);
    // Warning: uses unchecked or unsafe operations
  }
}

// 8. Simplify usage with type inference:

class GenericUseCases8<T> {
  private T type;
  public GenericUseCases8(T type) {
    this.type = type;
  } 
  public static <T> GenericUseCases8<T> inferType(T t) {
    return new GenericUseCases8<T>(t);
  }
  static void test() {
    GenericUseCases8<Waffle> w = inferType(new Waffle());
  }
}

// 9. Utilize capture conversion:

class GenericUseCases9 {
  public static void captureType(List<?> list) {
    explicitType(list);
  } 
  private static <T> void explicitType(List<T> list) {
    // ...
  }
  static void test() {
    captureType(new ArrayList<Waffle>());
  }
}

Talk Back!

Have an opinion? Readers have already posted 39 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Bruce Eckel adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

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.

This weblog entry is Copyright © 2005 Bruce Eckel. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

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