3

I have an interface Moded. The methods are getMode(), setMode( Enum<?> mode ).

It also has a method getMode( Object key ), which is used to identify the "mode type" in implementing classes when they have multiple modes (e.g. OPEN/CLOSED is one state, but COMMAND_MODE/QUERY_MODE/OTHER_MODE is another state, i.e. "2 dimensions" of mode).

The implementing classes will then use enums to provide the possible values of each mode type.

The idea is that getMode( Object key ) is passed a key which the implementing class uses to identify the mode type to find the value of. But to make this nicer it would be ideal if you always had to use the Enum class object as the key. You can do this anyway, but to enforce it, I've tried changing to getMode( Class<Enum<?>> key ).

For some reason, say I have a class IndexManager which implements Moded, and it contains an enum like this:

enum MANAGER_MODE {
    COMMAND_MODE, QUERY_MODE, OTHER_MODE
}

and my implemented method looks something like this:

@Override
public Enum<?> getMode(Class<Enum<?>> key ) throws Exception {

    if( key.equals( IndexManager.MANAGER_MODE.class )){
        return managerMode;
    }
    ...

... and I then try this:

indexManager.getMode( IndexManager.MANAGER_MODE.class )

I'm getting a message in Eclipse

The method getMode(Class<Enum<?>>) in the type IndexManager is not applicable for the arguments (Class<IndexManager.MANAGER_MODE>)

And yet this says "true":

String.format( "extends Enum? %s", Enum.class.isAssignableFrom( IndexManager.INDEX_MANAGER_MODE.class ))

Later

In fact the answer by Andreas is better (no casting; type safety if required). Using his approach you put

<T extends Enum<T>> T getMode(Class<T> key ) 

to allow implementation of multi-mode classes

and

<T extends Enum<T>> T getMode()

to allow implementation of single-mode classes

mike rodent
  • 14,126
  • 11
  • 103
  • 157

2 Answers2

4

Try getMode(Class<? extends Enum> key) instead.

Terje
  • 1,753
  • 10
  • 13
  • Sir/Madam, you are a genius of parameterisation. I take my hat off to you. Actually I'm putting `getMode( Class extends Enum>> key )` to avoid a message about "raw types". – mike rodent Jan 13 '17 at 19:13
  • @mikerodent For an explanation of this, see http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p . – VGR Jan 13 '17 at 19:14
  • @VGR Tx - will study - I've probably read up on this before, but this sort of not particularly common parameterisation stuff tends to go in one ear and out the other with me. – mike rodent Jan 13 '17 at 19:17
1

To allow the caller to assign the return value to a variable of the given type, you need to replace the wildcards with a template parameter.

E.g. if you want to support this call:

MANAGER_MODE mode = indexManager.getMode( MANAGER_MODE.class );

Then your method needs to be like this:

@SuppressWarnings("unchecked")
public <T extends Enum<T>> T getMode(Class<T> key) {
    if (key == MANAGER_MODE.class)
        return (T) managerMode;
    throw new IllegalArgumentException("Unknown mode type: " + key.getName());
}
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • Actually, can I just ask you about this, because I've tried experimenting with it. Doesn't this pattern mean that `Moded` has to be parameterised: `Moded>`? But given than `Moded` can refer to multiple `Enum` classes this doesn't work. Otherwise can you show me how the interface method looks and how the implementing class method looks (without compiler complaints)? – mike rodent Jan 14 '17 at 08:41
  • Isn't that what I've already done? Show how `getMode()` would be implemented? – Andreas Jan 14 '17 at 08:46
  • 1
    There is no need for an unchecked operation. You can use `return key.cast(managerMode);` instead. – Holger Jan 17 '17 at 16:21