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