Java Generics and Defining Type Bounds

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

floodslayer
Posts: 28
Joined: Sat Mar 21, 2009 12:17 am UTC

Java Generics and Defining Type Bounds

Postby floodslayer » Mon Nov 29, 2010 2:50 pm UTC

Hello all,

So I've got a pretty solid understanding of working with generics in Java. I know how to declare generic type variables, and have defined multiple classes with complex generic types as well, so I feel like I understand at least 90% of what is possible with generics. However, something recently came up and I've been bashing my head against a wall trying to accomplish it with generics and hoping to avoid defining one-off preconditions and running runtime casting.

What I want to do is declare an abstract class with a generic type (no problem so far), but I also want to set the type bounds so that any concrete implementation of the abstract class also matches the generic type. I've prepared an example of some code which tries (and fails) to do this.

Code: Select all

/**
 * A class which can eat a generic type of food
 */
interface Eater<Food> {
   void eatFood(Food f);
}

/**
 * A class which can always eat itself
 * Type bounds don't work correctly
 */
abstract class AbstractCannibal<Food extends AbstractCannibal<Food>>
   implements Eater<Food>{

   @Override
   public void eatFood(Food f) {
      // TODO Auto-generated method stub
   }
   
   public void eatSelf() {
      eatFood(this);   //this line will not compile
   }
}

/**
 * An example of a Cannibal which works as expected, the PickyCannibal
 * will only eat other PickyCannibals, so it may also eat itself
 */
class PickyCannibal extends AbstractCannibal<PickyCannibal> {
   public void eatSelf() {
      eatFood(this);
   }
}

/**
 * An example of a Cannibal which does not work as expected, the AngryCannibal
 * will only eat PickyCannibals, so it may not eat itself.  Ideally this would not
 * satisfy the AbstractCannibal type bounds, but the bounds are incorrect
 */
class AngryCannibal extends AbstractCannibal<PickyCannibal> {
   public void eatSelf() {
      eatFood(this);   //this line will not compile
   }
}

/**
 * Alternative, but it doesn't accomplish everything I want
 */
abstract class NonGenericCannibal
   implements Eater<NonGenericCannibal> {
   
   @Override
   public void eatFood(NonGenericCannibal f) {
      /*
       * Everything compiles here, but no subclass of NonGenericCannibal
       * can use methods on 'f' which don't exist in NonGenericCannibal
       * without performing a type cast
       */
   }
   
   public void eatSelf() {
      eatFood(this);
   }
}


I see why my AbstractCannibal class doesn't work. A concrete class can declare it's Food parameter such that it meets the type bounds I've set, but the concrete class doesn't satisfy that bound (like in AngryCannibal). The question is, is it possible to modify the type bounds on AbstractCannibal such that it does what I want, without having to mask the runtime type of the provided "food" (like what happens with NonGenericCannibal).

Token
Posts: 1481
Joined: Fri Dec 01, 2006 5:07 pm UTC
Location: London

Re: Java Generics and Defining Type Bounds

Postby Token » Mon Nov 29, 2010 3:17 pm UTC

So, judging by your comment in NonGenericCannibal, you want an abstract class A that implements Eater<A>, such that any B extending A will by default implement Eater<B> rather than Eater<A>? I don't believe you can...
All posts are works in progress. If I posted something within the last hour, chances are I'm still editing it.

floodslayer
Posts: 28
Joined: Sat Mar 21, 2009 12:17 am UTC

Re: Java Generics and Defining Type Bounds

Postby floodslayer » Mon Nov 29, 2010 3:32 pm UTC

Token wrote:So, judging by your comment in NonGenericCannibal, you want an abstract class A that implements Eater<A>, such that any B extending A will by default implement Eater<B> rather than Eater<A>? I don't believe you can...


That's it exactly. I didn't figure there was a straightforward way, but I figured it might be worth asking.

Rysto
Posts: 1460
Joined: Wed Mar 21, 2007 4:07 am UTC

Re: Java Generics and Defining Type Bounds

Postby Rysto » Tue Nov 30, 2010 1:35 am UTC

It can't be done. If A implements Eater<A> and B extends A, B must implement Eater<A>. You might be able to get away with making A implement Eater<? extends A> and do some deep magic to force B to implement Eater<? extends B>, but I'm blanking on how exactly you'd do that.

floodslayer
Posts: 28
Joined: Sat Mar 21, 2009 12:17 am UTC

Re: Java Generics and Defining Type Bounds

Postby floodslayer » Tue Nov 30, 2010 11:32 am UTC

Interesting suggestion Rysto. I just tried putting a wildcard in the 'implements' clauses, but it won't compile.
The type AbstractCanibal cannot extend or implement Eater<? extends Food>. A supertype may not specify any wildcard.

I've also tried redeclaring the interface implementation on one of the subclasses using a narrower type specification, but this is also not allowed. You cannot implement the same interface more than once on different generic types.

It looks more and more like this isn't going to be possible.

Token
Posts: 1481
Joined: Fri Dec 01, 2006 5:07 pm UTC
Location: London

Re: Java Generics and Defining Type Bounds

Postby Token » Tue Nov 30, 2010 4:43 pm UTC

So the question becomes - why do you *need* it to be possible? There may well be a more sensible way of doing what you're trying to do...
All posts are works in progress. If I posted something within the last hour, chances are I'm still editing it.

floodslayer
Posts: 28
Joined: Sat Mar 21, 2009 12:17 am UTC

Re: Java Generics and Defining Type Bounds

Postby floodslayer » Tue Nov 30, 2010 7:01 pm UTC

I'm knocking together a graph traversal algorithm (A*) and I thought it would be nice to be able to model it as a set of states and available state transitions. The priority queue and comparator to choose which node to explore next are all done, but I'm writing the code which determines a list of states reachable from the current state. What I've written right now looks like this

Code: Select all

public Collection<T> getTransitionStates() {
      ArrayList<T> ret = new ArrayList<T>(transitions.size());
      for(AStarStateTransition<T> trans : transitions) {
         try {
            ret.add(trans.getPostTransitionState(this));
         } catch (IllegalStateTransitionException iste) {
            continue;
         }
      }
      return ret;
   }


where THIS is a general purpose A* state and transitions has a list of possible "moves". The getTransitionStates method should produce a collection of states which can be reached from the current state by performing each move on the current state (if possible). However, getPostTransitionState expects and returns a T and THIS does not meet the capture of T, which is defined as

Code: Select all

public class GenericAStarState<T extends GenericAStarState<T>>
   implements AStarState<T>


The goal was to define the type bounds on T in such a way that I know a GenericAStarState will also always by a T, but it doesn't seem that this is possible.


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 8 guests