【问题标题】:Kotlin fails to get the right enum instanceKotlin 无法获得正确的枚举实例
【发布时间】:2021-04-18 19:33:42
【问题描述】:

所以我们正在使用 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
}

结果相同。

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

什么给了?

为什么会这样?

我该如何让它发挥作用?

【问题讨论】:

  • 嘿抱歉问,CONTROL(VaadinKey.CONTROL); 在 VaadinKeyModifier 中究竟应该做什么?顺便说一句,当您调用 VaadinKeyModifier.CONTROL 时,它应该调用其父级的 CONTROL 属性,因为 CONTROL(VaadinKey.CONTROL); 实际上什么都不做
  • @ErfanDP 好吧,它应该定义一个名为CONTROLVaadinKeyModifier 枚举常量,它以VaadinKey.CONTROL 作为其构造函数参数。

标签: java kotlin enums compiler-errors type-inference


【解决方案1】:

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

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

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

【讨论】:

  • 想详细说明一下?我确实提到这两个类是 Vaadin 框架的一部分,所以我不确定我应该如何重命名枚举或分离接口。 (除非您的意思是我们应该将类复制到我们自己的代码中并忽略框架提供的类,我们不会这样做,因为框架不会接受我们期望的实例。)
  • 用可能的解决方法更新了我的答案
  • 这对于解决方法来说非常漂亮。谢谢!
猜你喜欢
  • 2014-02-04
  • 2020-02-09
  • 2023-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多