Reputation: 8182
So we are using Vaadin. Vaadin comes with a Key
interface. I copied it and deleted all entries except the CONTROL
key (for demonstration purposes here, we don't copy the classes in our actual code):
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
@FunctionalInterface
public interface VaadinKey extends Serializable {
VaadinKey CONTROL = VaadinKey.of("Control", "ControlLeft", "ControlRight");
//...
static VaadinKey of(String key, String... additionalKeys) {
Objects.requireNonNull(key);
if ("".equals(key)) {
throw new IllegalArgumentException("'key' cannot be empty");
}
List<String> keys = new ArrayList<>();
keys.add(key);
keys.addAll(Arrays.asList(additionalKeys));
return () -> keys;
}
List<String> getKeys();
default boolean matches(String key) {
return getKeys().contains(key);
}
static boolean isModifier(com.vaadin.flow.component.Key key) {
return Stream.of(VaadinKeyModifier.values())
.anyMatch(k -> k.matches(key.getKeys().get(0)));
}
}
The interface is implemented by a KeyModifier
class, that I've also copied out and stripped down:
import java.util.List;
import java.util.stream.Stream;
public enum VaadinKeyModifier implements VaadinKey {
/**
* KeyModifier for "{@code Control}" key.
*/
CONTROL(VaadinKey.CONTROL)
//...
;
private final VaadinKey key;
VaadinKeyModifier(VaadinKey key) {
this.key = key;
}
@Override
public List<String> getKeys() {
return key.getKeys();
}
public static VaadinKeyModifier of(String key) {
return Stream.of(values()).filter(k -> k.matches(key)).findFirst()
.orElseThrow(IllegalArgumentException::new);
}
}
Now let's try to use it from Kotlin:
fun foo() {
val ctrl: VaadinKeyModifier = VaadinKeyModifier.CONTROL
}
This will fail to compile because
Type mismatch: inferred type is VaadinKey! but VaadinKeyModifier was expected
(And if I ctrl+click it in IntelliJ, it will also take me to the VaadinKey
interface.)
I also tried
import com.[...].VaadinKeyModifier.CONTROL as CONTROL_MODIFIER
fun foo() {
val ctrl: VaadinKeyModifier = CONTROL_MODIFIER
}
With the same result.
Now interestingly, the direct use of VaadinKeyModifier.CONTROL
will not register any usage of VaadinKeyModifier::CONTROL
, whereas the import
variant registers one usage in the import (which however does not prevent the compilation error).
What gives?
Why does this happen?
And how do I get this to work?
Upvotes: 2
Views: 429
Reputation: 7882
Name of your enum instance (CONTROL
) clashes with its self-titled field inherited from the interface.
In Java this ambiguity resolves in favor of enum instance, in Kotlin (during Java interop) for some reason it resolves in favor of field. However if VaadinKeyModifier
class was defined in Kotlin, it would have been resolved in favor of enum:
enum class VaadinKeyModifierKT(private val key: VaadinKey) : VaadinKey {
CONTROL(VaadinKey.CONTROL);
}
val ctrl: VaadinKeyModifierKT = VaadinKeyModifierKT.CONTROL //Would be compiled
If changing given code to avoid this ambiguity (for instance, via renaming fields/enums or separating interface and these fields) is not an option, then you can access instances of VaadinKeyModifier
via its name:
val ctrl = enumValueOf<VaadinKeyModifier>("CONTROL")
As a less error-prone option, I'd suggest creating auxilary enum, duplicating values of VaadinKeyModifier
, with converter to VaadinKeyModifier
type:
enum class Modifier {
SHIFT,
CONTROL,
ALT,
ALT_GRAPH,
META;
fun toVaadinKeyModifier() : VaadinKeyModifier = enumValueOf(name)
}
//Usage:
val ctrl: VaadinKeyModifier = Modifier.CONTROL.toVaadinKeyModifier()
Upvotes: 2