Wednesday 12 March 2008

Hangman with Generics

I'm implementing a hangman game, which uses a HashMap to store a character and result of the character guess.


public abstract class Guess{}

public class CorrectGuess extends Guess{}

public class FailedGuess extends Guess{}

public class Hangman {
private Map<Character,Guess> state = new HashMap<Character,Guess>();
}


I want to be able to get a list of the various correct and failed guesses, so the details can be displayed.

The nasty solution i've currently got is to use "instanceof", but i feel this misses the point of using generics.


// the calling code looks something like
void display()
{
hangmanDisplay.display(getFailedGuesses());
consoleDisplay.display(getCorrectGuesses());
}

public List<CorrectGuess> getCorrectGuesses()
{
List<CorrectGuess> list = new ArrayList<CorrectGuess>(secret.length());
for(Iterator iterator = state.values().iterator();iterator.hasNext();)
{
Guess guess = (Guess) iterator.next();
if(guess instanceof CorrectGuess)
list.add((CorrectGuess)guess);
}
return list;
}

public List<FailedGuess> getFailedGuesses()
{...}


I want to be able to use the generic parameter type to control what is added to the list.


// the calling code would then look something like
void display()
{
hangmanDisplay.display(getGuesses(new ArrayList<FailedGuess>()));
consoleDisplay.display(getGuesses(new ArrayList<CorrectGuess>());
}

/**
* get all values of generic parameter type T in the HashSet and add to the list.
*/
public void getGuesses(List<? super Guess> list)
{
state,values().iterator();
// how can i compare the type thats in the iterator.next() with the type defined by T in the input parameter?
}

1 comment:

Unknown said...

Yo Emers!

My initial thoughts on this are:

* You are trying to use generics in way that they are not intended for. A generic collection allows you to assert things about the elements within that collection because you know that they are of a certain type. In this case, though, the point at which you are hoping the generic will help you out is exactly where you stop using generics. Generics are telling you that the collection contains Guess elements but not which subtype of Guess each element is -- because you have explicitly created a Collection of Guesses. If you want to find out what subtype each element is you must (as you have discovered) do something like instanceof to determine this. Using type-specific logic in this way is, of course, ugly as a screaming pig-dog. If you want type specific behaviour then that behaviour belongs on the subtype itself. You could add, for example, an abstract boolean isCorrect() method on the abstract type and implement it with either return true or return false in the appropriate subtype. This is, of course, a purely "classical" approach rather than a generics based approach, but I would argue that this appropriate because we are talking subtype-dependent behaviour rather than general behaviour that you want to apply across a range of types.

* The for loop over a collection is much more cleanly expressed with the new for( : ) loop.

* Surely in a game of hangman there is only a single correct answer, then the game ends? Why do you need a collection of correct guesses?

Finally, and far more importantly, why are you sending me emails with Java questions rather than questions along the lines of, "I'm in town! Fancy a few beers?"

Later.
R