3

I've been working on a project in Java lately that uses a Generic-Best Search Algorithm. To make the Algorithm itself Generic, I used a bunch of Generics on all of the Classes used in the Algorithm.

Here's all of the Class/Interface Declarations:

public class StateSearch<E extends AbstractState<E>>
public class AbstractState<E> implements State<E>
public class StateNode<E extends AbstractState<E>> implements Comparable<StateNode<E>>
public class StateQueue<E extends AbstractState<E>>
public interface State<E> extends ComparableState<E>>

Now in theory, all of this works just fine. However, when I apply the Algorithm, I plan for 'E' to be some sort of Game State (Say a Checkers Board, Solitaire Game, Pathfinder, what-have-you). Thus, I created the following Class:

public class GameState extends AbstractState<String>

My intention for this particular setup is to have the GameState be a container for a String which represents the State of a particular Game. However, this causes an issue for trying to create a StateSearch using the following code:

new StateSearch<GameState>(new GameState(initialState), new GameState(goalState));

I end up getting a Bound Mismatch Error, stating that GameState is not a valid substitute for <E extends AbstractState<E>>. Now, I believe I understand why this is happening. My thoughts is because GameState extends AbstractState<String>, and not AbstractState<E>.

Unfortunately, I do not want my GameState Class to have a Generic Type. It is designed to be implemented to actually do something, thus the switch from Generics to Strings. For example, I might want to create a GameState Class in an entirely different project that uses Integers for its implementation.

Given the Methods, Variables, and Operations I use and perform within the GameState Class, it cannot be Generic.

My question after all of this: Is there a way to implement my GameState Class as a Non-Generic Class in such a way that it satisfies the Bounding Requirements placed by my StateSearch Class?

I wouldn't mind if I had to change some of the Class Declarations. The point is that I need the Algorithm to be Generic, and the Implementation to be Non-Generic. The GameState Class needs to be the point at which I transition between the two.


Edit:
I need the GameState Class to function as a the implementation of a certain Game. The AbstractState and State interfaces are meant to allow me to build a State Search Algorithm without requiring the actual implementation. Settings things up in this manner allow me to apply this setup across multiple games.

The StateSearch Class basically just pulls States off of my StateQueue, which is just a Vector of AbstractState Classes that is ordered by a priority system (hence the Comparable implementation). The manner of which the State Tree is expanded is by using the getNextState() function from the AbstractState Class. This is fine and all, but the getNextState() function basically grabs the best state from my getSuccessors() function, which can only be defined in the GameState Class. Everywhere else it's just Abstract.

I cannot define the getSuccessors() function in a Generic manner, as it is purely based upon implementation. This function is meant to return all possible states from the current state. For example, if the game was Tic-Tac-Toe, the getSuccessors() function would return a list of Game States where each State represents a possible move.

The GameState Class also contains a getEstimatedCost() and getRunningCost() which basically act as the h(x) and g(x) functions respectively. This too may only be defined in the GameState Class, as both depend upon Implementation.

Boom
  • 2,465
  • 4
  • 19
  • 21

3 Answers3

3

The way you have things defined is effectively an implementation of the Curiously recurring template pattern

When you have:

public class StateSearch<E extends AbstractState<E>>

It means that to use a GameState as its Generic type, it would need to be defined as:

public class GameState extends AbstractState<GameState>

As for what you really need, it's unclear why you need generics. Why wouldn't a GameState interface suffice?

Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • 1
    +1 It's also possible the OP didn't intend to use the CRTP. `public class StateSearch, T>` is another possible solution. – Paul Bellora Feb 02 '14 at 04:55
  • Unfortunately a `GameState` interface wouldn't work. I need to be a typical class. Now, if I can only achieve this by creating an Interface, that's fine. – Boom Feb 02 '14 at 05:20
  • @Boom No state interface is necessary. No state is strictly necessary at all. – William Morrison Feb 02 '14 at 22:24
1

A search algorithm needs 2 things:

  1. Data (A connected graph)
  2. A way to compare data (nodes in the graph)

Each node knows about its connected nodes, representing a graph. And nodes are comparable to one another. This satisfies the 2 things a searching algorithm needs.

So, in our design, it's pretty natural for nodes to give this information to the searching algorithm. All other node state should be hidden from the searching algorithm to keep with good OO principals (encapsulation.)

For these reasons, I suggest refactoring to this simpler design.

//Comparable interface satisfies measuring a node's value.
//getAdjacentNodes abstract method exposes graph structure.
abstract class StateNode implements Comparable<StateNode>{
    abstract StateNode[] getAdjacentNodes();
}

//StateSearch can perform any search algorithm it needs StateNode.
class StateSearch<E extends StateNode>

//StateQueue is probably unnecessary. Consider replacing with a PriorityQueue.
class StateQueue<E extends StateNode> queue;

Whether a StateNode uses internal state at all is the implementing code's decision. In the example below, GameNode does keep game state, but the search algorithm is ignorant of this. All it knows is the minimum it needs to know; what StateNode exposes. If I'd left state out completely, the searching algorithm would be unaffected.

public class GameNode extends StateNode{
    String state;
    public GameNode(String state){
        this.state = state;
    }

    public StateNode[] getAdjacentNodes(){
        //return adjacent nodes.
    }

    public String getGameState(){
        return state;
    }
    public int compareTo(StateNode other){
        //comparison code
    }
}
William Morrison
  • 10,953
  • 2
  • 31
  • 48
  • This might be a step in the right direction. Also, I can't use a Priority Queue for this, as the Search Algorithm itself is also generic. I need to be able to switch between Depth-First-Search, Breadth-First-Search, Greedy-Search, Uniform-Cost-Search, A*, and whatever else I want. – Boom Apr 08 '15 at 17:59
-3

To me, the Java Generic mechanism provides most of its benefit by making the collections library possible. It seems to be developed against that sort of use case - which amply justifies it - and it only just manages that. If you look at an implementation of a collection cast, you will probably see either compile warnings or annotations to turn warnings off because of things like the inability to create generic arrays. Note that, because it works by type erasure, and not code generation, it guarantees no code bloat, but does not provide extra efficiency as in C++ - although you do get the benefit of some guarantees about type cast exceptions never happening at run time. When I use it, I usually try and use it for something similar to collections, and I try and take them as a model.

I suspect that it is not possible to do everything you want with generics, and I would be wary of trying to do too much - as I would of any language feature that you don't use regularly enough to become reflex-level proficient in. Stuff that takes you an hour to work out now will take you much longer to understand when you come back to modify or extend that code later.

mcdowella
  • 19,301
  • 2
  • 19
  • 25