On 15/06/17 15:43, Peter Levart wrote:
Hi Maurizio,

On 06/15/2017 03:55 PM, Maurizio Cimadamore wrote:
I get what you are saying. But I think this intermediate supertype feels too magic. After all, you're after a sneaky way to extend from an enum - which is not something you can do in the source code. If it was possible, then you could just add the supertype yourself

e.g.

enum Option {

D extends Generic<String>

...
}

where Generic would be defined as the translated code you wrote.

But since Generic cannot be written, you need to put that in the Option declaration, which seems untidy - in other words, if I look at the class declaration for Generic, there's nothing which tells me that it extends from Option, which is a big red herring IMHO (what if Generic is in a different sourcefile?).

The constraint would be that Generic can only be in the same compilation unit - i.e. as a static member class of the enum which designates it as a super type of constants. But I see your point - it would not have an "extends Option" clause in its declaration (the enum declaration would designate it as a subclass of enum type from the other way around), so it would be easy to misinterpret it.

Also, you open up issues where changing your enum declaration is effectively changing the supertype of a (seemingly) unrelated class - meaning that if you have code outside which relied on that Generic class, you could break them w/o changing the definition of Generic.

That's why I think adding custom superinterfaces would be slightly tidier - as the information goes exactly in the place where you need it.


Regarding your point about casting with the 'get' method - well, it seems to me the cast would be internal to the API - so it's not something the client would see (e.g. the caller of 'get' would be able to pass the enum constant just fine).

Maurizio

...yeah, but the implementation of get would have to cast, meaning that there's something inadequate with the representation. Generics were meant to reduce the need of explicit casts.

I think there is a solution though which does not even require sealed interfaces. Take your example with Generic<T> as plain interface:

enum Option {
   D implements Generic<String>("-d", ...) { ... }
   PROC implements Generic<ProcOption>("-proc", ...) { ... }

   ...

}


...and then use the following method signature:

public <Z, O extends Option & Generic<Z>> Z get(O option) { ... }


Yes - that's even better :-)

Maurizio
Regards, Peter




On 15/06/17 14:07, Peter Levart wrote:


On 06/15/2017 02:44 PM, Peter Levart wrote:

enum Option implements Consumer<String> {
   D implements Generic<String>("-d", ...) { ... }
   PROC implements Generic<ProcOption>("-proc", ...) { ... }

   ...

}

Which is not too terrible (in fact has been put forward by John as a comment in the JEP [1]).


This is similar, but not the same. In above example, Generic<T> is not a subtype of Option. It's just an interface implemented by constant's subclasses. So you can not access Option members via an instance of Generic<T>... Generic<T> therefore has to declare all the interesting methods that can then be implemented by Option. You also have to accompany this solution with "sealed" interfaces if you don't want other implementations of Generic<T> besides the enum constants...

...in addition, you can not access/modify elements of EnumMap<Option, ...> using keys of type Generic<T> for example - you would have to cast which is awkward given that your example use case:

public Z get(Generic<Z> option) { ... }

...would probably do just that...

Regards, Peter




Reply via email to