首页 > 解决方案 > Kotlin 无法获得正确的枚举实例

问题描述

所以我们正在使用 Vaadin。Vaadin 带有一个Key界面。我复制了它并删除了除CONTROL键之外的所有条目(出于演示目的,我们不复制实际代码中的类):

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类实现,我也将其复制并剥离:

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
}

这将无法编译,因为

类型不匹配:推断类型是 VaadinKey!但 VaadinKeyModifier 是预期的

(如果我在 IntelliJ 中 ctrl+单击它,它也会带我进入VaadinKey界面。)

我也试过

import com.[...].VaadinKeyModifier.CONTROL as CONTROL_MODIFIER

fun foo() {
    val ctrl: VaadinKeyModifier = CONTROL_MODIFIER
}

结果相同。

现在有趣的是,直接使用 ofVaadinKeyModifier.CONTROL不会注册 的任何用法VaadinKeyModifier::CONTROL,而import变体在导入中注册了一种用法(但这并不能防止编译错误)。

是什么赋予了?

为什么会这样?

我如何让它发挥作用?

标签: javakotlinenumscompiler-errorstype-inference

解决方案


枚举实例的名称 ( CONTROL) 与其从接口继承的同名字段冲突。

在 Java 中,这种歧义解决了有利于枚举实例,在 Kotlin(在 Java 互操作期间)由于某种原因它解决了有利于字段。然而,如果VaadinKeyModifier类是在 Kotlin 中定义的,它会被解决为有利于枚举:

enum class VaadinKeyModifierKT(private val key: VaadinKey) : VaadinKey {
    CONTROL(VaadinKey.CONTROL);
}

val ctrl: VaadinKeyModifierKT = VaadinKeyModifierKT.CONTROL //Would be compiled

如果更改给定代码以避免这种歧义(例如,通过重命名字段/枚举或分离接口和这些字段)不是一种选择,那么您可以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()

推荐阅读