tags:

views:

1330

answers:

8

Can anyone point me to good examples of using generics in Java? By this I mean examples of writing a generic class oneself?

Most explanations read "You can define a generic class like this. Now see the Java Collections API and forget all that - just use it and be happy."

What I want is more like "You can define a generic class like this, now consider this case where you might want it, now let's see how we'd write it."

Thanks.

A: 

A couple of useful exercises:

  • write your own ArrayList implementation
  • write a basic Map implementation (no need to worry about efficiency there, if you need to traverse a whole array to find something, do it)
  • subclass your Map impl. to count value writes for a key, with a custom Map.Entry that has a method that returns the write count

I can't give you code examples right away, because I am working on them, but maybe you could try to do them yourself, and come back with further questions if you are stuck.

Varkhan
Any low level stuff that you'd actually want to implement with arrays is probably a poor example.
Tom Hawtin - tackline
+3  A: 

Your answer is partially answered in the Java Generics FAQ, that gives some good examples of when you want your class to be a generic class. Also check out the section Designing Generic Methods in that FAQ.

Eddie
+1  A: 

I've used generics to deal with some basic CRUD operations in a DAO. Much of the code would have been duplicated for various domain objects, so as much as possible I extracted out into a generic abstract class.

Adam Jaskiewicz
+1 for this. If you use Hibernate and Java, there's a nice article from IBM (surprise) on the topic: http://www.ibm.com/developerworks/java/library/j-genericdao.html
duffymo
+1  A: 

Not sure this is exactly what you're looking for, but as for examples... any sort of data structure you create can (and probably should) be done in generic form. I once started one like this:

public class TrieMap<C extends Comparable<C>, K extends ComponentComparable<C>, V>
 extends AbstractMap<K,V> implements SortedMap<K,V> {
    ...
}

And of course, that depended on another use of generics, this interface for sequences whose elements are mutually comparable:

public abstract interface ComponentComparable<C extends Comparable<C>>
 extends Comparable<ComponentComparable<C>>, Iterable<C> {
    ...
}

I also created a whole set of node classes like

public interface Node<T extends Node<T>> { ... }
public interface TreeNode<T extends TreeNode<T>> extends Iterable<T>, Node<T> { ... }
public interface ListNode<T extends ListNode<T>> extends Node<T> { ... }
public interface BinaryTreeNode<T extends BinaryTreeNode<T>> extends Node<T> { ... }
public interface TernaryTreeNode<T extends TernaryTreeNode<T>>
 extends BinaryTreeNode<T> { ... }

which could conceivably come in handy for creating graphs, trees, linked lists, etc. along with an Algorithms class that has all sorts of (generic!) methods for operating on these nodes.

Or if you're sick of data structures, some other ideas include an interface for things that can be converted to a type:

public abstract interface Convertable<T> {
    public abstract T convert();
}

or one for string parsers:

public interface Parser<O> {
    public abstract O parse(String str);
}

or one for classes that are their own parsers:

public interface Parseable<P extends Parseable<P>> extends Parser<P> {}

which I used in creating a system to parse command-line options:

public abstract class Option<T> { ... }
public class CLOption<P extends Parseable<P>> extends Option<P> { ... }
public class StringOption extends Option<String> { ... }

etc. etc. etc.

David Zaslavsky
+1  A: 
public class Pair<A,B> {
    private A a;
    private B b;
    public Pair(A a, B b) { setFirst(a); setSecond(b); }    
    public A getFirst() { return a; }
    public B getSecond() { return b; }
    public void setFirst(A a) { this.a=a; } 
    public void setSecond(B b) { this.b=b; }
}
KitsuneYMG
I like to make it immutable myself.
Michael Myers
+3  A: 

Generics in Java facilitate parametric polymorphism. By means of type parameters, you can pass arguments to types. Just as a method like String foo(String s) models some behaviour, not just for a particular string, but for any string s, so a type like List<T> models some behaviour, not just for a specific type, but for any type. List<T> says that for any type T, there exists a List of Ts. So List is a actually a type constructor. It takes a type as an argument and constructs another type as a result.

Here are a couple of examples of generic types I use every day. First, a very useful generic interface:

public interface F<A, B> {
  public B f(A a);
}

This interface says that for any two types, A and B, there's a function (called f) that takes an A and returns a B. When you implement this interface, A and B can be any types you want, as long as you provide a function f that takes the former and returns the latter. Here's an example implementation of the interface:

F<Integer, String> intToString = new F<Integer, String>() {
  public String f(int i) {
    return String.valueOf(i);
  }
}

Before generics, polymorphism was achieved by subclassing using the extends keyword. With generics, we can actually do away with subclassing and use parametric polymorphism instead. For example, consider a parameterised (generic) class used to calculate hash codes for any type. Instead of overriding Object.hashCode(), we would use a generic class like this:

public final class Hash<A> {
  private final F<A, Integer> hashFunction;

  public Hash(final F<A, Integer> f) {
    this.hashFunction = f;
  }

  public int hash(A a) {
    return hashFunction.f(a);
  }
}

This is much more flexible than using inheritance, because we can stay with the theme of using composition and parametric polymorphism without locking down brittle hierarchies.

Java's generics are not perfect though. You can abstract over types, but you can't abstract over type constructors, for example. That is, you can say "for any type T", but you can't say "for any type T that takes a type parameter A".

I wrote an article about these limits of Java generics, here.

The source for a lot of useful generic types can be found here.. Also check out the source for the standard Java library, if you haven't already.

Apocalisp
+1  A: 

An example of a static utility function using generics. It takes two maps, A -> B and B -> C and constructs an A -> C (hash) map from it.

public static <A, B, C> Map<A, C> transitise(Map<A, B> aToB, Map<B, C> bToC) {
    HashMap<A, C> map = new HashMap<A, C>(aToB.size() * 2);
    for (Map.Entry<A, B> abEntry : aToB.entrySet()) {
     map.put(abEntry.getKey(), bToC.get(abEntry.getValue()));
    }
    return map;
}

Example Use:

public static void main(String[] args) {
    HashMap<String, Integer> nameToNumber = new HashMap<String, Integer>();
    nameToNumber.put("Anna", 12345);
    nameToNumber.put("James", 444);
    HashMap<Integer, Point> numberToPosition = new HashMap<Integer, Point>();
    numberToPosition.put(12345, new Point(3, 3));
    numberToPosition.put(444, new Point(1, 55));
    for (Map.Entry<String, Point> nTP :
  transitise(nameToNumber, numberToPosition).entrySet())
 {
     System.out.println(nTP.getKey() + " -> " +
   nTP.getValue().x + "/" + nTP.getValue().y);
    }
}

Results in:

James -> 1/55
Anna -> 3/3

Anyway, what I'm trying to illustrate is that using generics, you can make some very useful, er, generic utility functions.

Zarkonnen
A: 

I think good examples of generics are the collection classes of Java SE.
You got good documentation and a lot of source code to examine.

Carlos Heuberger