10

I'm attempting to have a collection of enums that extend a common interface, so something like:

interface Fooable
{
  void someCommonMethod();
}

enum E1 implements Fooable
{
   // some enumuerations and a definition for someCommonMethod()
}

enum E2 implements Fooable
{
   // some different enumerations and a different definition for someCommonMethod()
}

and then make use of this elsewhere by enforcing both that a variable is a Enum and implements the interface. So something along the lines of..

bar(Enum<? extends Fooable> fe)
{
  fe.ordinal();
  fe.someCommonMethod();
}

However, so far I seem to have to cast fe in order to treat it as implementing the interface, i.e.,

bar(Enum<? extends Fooable> fe)
{
  fe.ordinal();
  ((Fooable)fe).someCommonMethod();
}

and while this should be safe... it seems suboptimal and that I may be overlooking something. Of course if I try to just pass the param as a Fooable then I wind up casting to treat it as a Enum and not only is this no-gain I'm now not even safe. See following:

bar(Fooable fe)
{
  // potentially unsafe cast!
  ((Enum<?>)fe).ordinal();
  fe.someCommonMethod();
}

Is there anything I'm overlooking or is the

Enum<? extends Fooable>

about as close to a 'good' solution as I'll get?

I am relatively new to Java and am still catching myself trying to use it like C or C++ so if I'm treating it like a hammer instead of a saw or overlooking something stupidly simple feel free to point it out :)

Khanmots
  • 171
  • 1
  • 10

2 Answers2

20

This means that T extends Enum and implements Fooable:

<T extends Enum<T> & Fooable>

Thus your method can be written as:

<T extends Enum<T> & Fooable> void bar(T fe) {
    fe.ordinal();
    fe.someCommonMethod();
}
Håvard Geithus
  • 5,167
  • 5
  • 30
  • 49
  • yeah, but my understanding is that's only for use when declaring a generic not defining a parameter to a method? – Khanmots Aug 10 '12 at 21:58
  • 2
    @Khanmots [Methods themselves can be generic](http://docs.oracle.com/javase/tutorial/extra/generics/methods.html). – Paul Bellora Aug 10 '12 at 21:58
  • Hadn't thought of that approach... had experimented with having the interface generic with a T extends Enum & Fooable, but may have to go poke around with making the enclosing class generic so that all of it's methods can have a T with both constraints... – Khanmots Aug 10 '12 at 22:01
  • Unfortunantly this looks like it just creates additional unchecked casts in my codebase, namely some of the methods on the Fooable interface return enumerations that would then be assigned to a variable of type T... and due to type erasure this requires an unchecked cast. It does look like it'd provide for enforcing the dual constraint at compile time though... gonna have to think about this. – Khanmots Aug 10 '12 at 22:24
  • How would this change the return types of the methods in the Fooable interface? – Håvard Geithus Aug 10 '12 at 22:41
  • Consider the following line `T fe2 = fe.someOtherCommonMethod();` Even without considering things like covariant return types, I'm pretty sure that type erasure will result in the runtime being unable to ensure that the type returned by someOtherCommonMethod() matches the type T. Or am I mistaken on this? – Khanmots Aug 10 '12 at 22:49
1

One option you have is to add any of the methods from Enum you need onto Fooable or create a new interface that extends Fooable and adds the Enum methods you need.

Example:

interface Fooable {
   void someCommonMethod();
}

interface FooableEnum extends Fooable {
   int ordinal();
}

enum E1 implements FooableEnum {
   // Implement someCommonMethod.
   // ordinal() is already implemented by default.
}

Once you've done this you can use FooableEnum as the parameter type in your method signature and not worry about any of the generic stuff.

Mike Deck
  • 17,069
  • 15
  • 62
  • 89
  • I'd thought about this, but I had reservations about it's behavior with respect to values(), may be worth revisiting though as right now I'm not able to dredge up a valid reservation... – Khanmots Aug 10 '12 at 22:29
  • This requires an unnecessary interface. – Håvard Geithus Aug 10 '12 at 22:55
  • @Mike, more I think on this the more I think it's the correct path. My requirement isn't really that the type **is** an enumm, it's that it has the **behavior** of an enum. I've also convinced myself that the values() thing isn't going to be a problem and was me being silly. – Khanmots Aug 10 '12 at 22:59
  • @Havard, depends on how the interface is being used. The int ordinal() can be placed in the original Fooable interface but if that Fooable interface were being made use of elsewhere without the requirement to be an enum that I'm attempting to impose here having the second interface as done here could be useful. In my case I'll likely be combining the two; but having it shown seperately was advantageous. – Khanmots Aug 10 '12 at 23:00