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()