Kotlin 无法获得正确的枚举实例
Kotlin fails to get the right enum instance
所以我们正在使用 Vaadin。 Vaadin 带有一个 Key
接口。我复制了它并删除了除 CONTROL
键之外的所有条目(此处出于演示目的,我们不会在实际代码中复制 classes):
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)));
}
}
该接口由KeyModifier
class实现,我也复制并剥离了:
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);
}
}
现在让我们尝试在 Kotlin 中使用它:
fun foo() {
val ctrl: VaadinKeyModifier = VaadinKeyModifier.CONTROL
}
这将无法编译,因为
Type mismatch: inferred type is VaadinKey! but VaadinKeyModifier was expected
(如果我在 IntelliJ 中 ctrl+click 它,它也会带我到 VaadinKey
界面。)
我也试过了
import com.[...].VaadinKeyModifier.CONTROL as CONTROL_MODIFIER
fun foo() {
val ctrl: VaadinKeyModifier = CONTROL_MODIFIER
}
同样的结果。
现在有趣的是,直接使用 VaadinKeyModifier.CONTROL
不会注册任何 VaadinKeyModifier::CONTROL
的用法,而 import
变体会在导入中注册一个用法(但不会阻止编译错误)。
什么给了?
为什么会这样?
我该如何让它发挥作用?
您的枚举实例的名称 (CONTROL
) 与其从接口继承的自命名字段冲突。
在 Java 中,由于某种原因,在 Kotlin 中(在 Java 互操作期间),这种歧义有利于枚举实例。但是,如果 VaadinKeyModifier
class 是在 Kotlin 中定义的,它会被解决以支持枚举:
enum class VaadinKeyModifierKT(private val key: VaadinKey) : VaadinKey {
CONTROL(VaadinKey.CONTROL);
}
val ctrl: VaadinKeyModifierKT = VaadinKeyModifierKT.CONTROL //Would be compiled
如果更改给定代码以避免这种歧义(例如,通过重命名 fields/enums 或分离接口和这些字段)不是一种选择,那么您可以通过其名称访问 VaadinKeyModifier
的实例:
val ctrl = enumValueOf<VaadinKeyModifier>("CONTROL")
作为一个不太容易出错的选项,我建议创建辅助枚举,复制 VaadinKeyModifier
的值,并将转换器转换为 VaadinKeyModifier
类型:
enum class Modifier {
SHIFT,
CONTROL,
ALT,
ALT_GRAPH,
META;
fun toVaadinKeyModifier() : VaadinKeyModifier = enumValueOf(name)
}
//Usage:
val ctrl: VaadinKeyModifier = Modifier.CONTROL.toVaadinKeyModifier()
所以我们正在使用 Vaadin。 Vaadin 带有一个 Key
接口。我复制了它并删除了除 CONTROL
键之外的所有条目(此处出于演示目的,我们不会在实际代码中复制 classes):
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)));
}
}
该接口由KeyModifier
class实现,我也复制并剥离了:
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);
}
}
现在让我们尝试在 Kotlin 中使用它:
fun foo() {
val ctrl: VaadinKeyModifier = VaadinKeyModifier.CONTROL
}
这将无法编译,因为
Type mismatch: inferred type is VaadinKey! but VaadinKeyModifier was expected
(如果我在 IntelliJ 中 ctrl+click 它,它也会带我到 VaadinKey
界面。)
我也试过了
import com.[...].VaadinKeyModifier.CONTROL as CONTROL_MODIFIER
fun foo() {
val ctrl: VaadinKeyModifier = CONTROL_MODIFIER
}
同样的结果。
现在有趣的是,直接使用 VaadinKeyModifier.CONTROL
不会注册任何 VaadinKeyModifier::CONTROL
的用法,而 import
变体会在导入中注册一个用法(但不会阻止编译错误)。
什么给了?
为什么会这样?
我该如何让它发挥作用?
您的枚举实例的名称 (CONTROL
) 与其从接口继承的自命名字段冲突。
在 Java 中,由于某种原因,在 Kotlin 中(在 Java 互操作期间),这种歧义有利于枚举实例。但是,如果 VaadinKeyModifier
class 是在 Kotlin 中定义的,它会被解决以支持枚举:
enum class VaadinKeyModifierKT(private val key: VaadinKey) : VaadinKey {
CONTROL(VaadinKey.CONTROL);
}
val ctrl: VaadinKeyModifierKT = VaadinKeyModifierKT.CONTROL //Would be compiled
如果更改给定代码以避免这种歧义(例如,通过重命名 fields/enums 或分离接口和这些字段)不是一种选择,那么您可以通过其名称访问 VaadinKeyModifier
的实例:
val ctrl = enumValueOf<VaadinKeyModifier>("CONTROL")
作为一个不太容易出错的选项,我建议创建辅助枚举,复制 VaadinKeyModifier
的值,并将转换器转换为 VaadinKeyModifier
类型:
enum class Modifier {
SHIFT,
CONTROL,
ALT,
ALT_GRAPH,
META;
fun toVaadinKeyModifier() : VaadinKeyModifier = enumValueOf(name)
}
//Usage:
val ctrl: VaadinKeyModifier = Modifier.CONTROL.toVaadinKeyModifier()