在从 Kotlin 调用的第三方 Java 库中存在重载解析模糊性。

huangapple go评论76阅读模式
英文:

Overload resolution ambiguity in third party java library called from kotlin

问题

我在Kotlin中编写了一个小程序,使用了库https://github.com/KaptainWutax/SeedUtils,具体使用了Dimension枚举https://github.com/KaptainWutax/SeedUtils/blob/master/src/main/java/kaptainwutax/seedutils/mc/Dimension.java

当我调用例如 Dimension.OVERWORLD.name 时,会出现 Overload resolution ambiguity 错误。
我知道问题出在哪里,问题是 enum Dimension 有一个 name 字段,而枚举类本身也有一个 name 字段 https://kotlinlang.org/docs/reference/enum-classes.html

问题是,我该怎么办?我当前的方法是分别fork这个库和其他我使用并依赖的4个库,然后将那个枚举中的 name 重命名为其他内容,但我不想为了重命名一个字段而不得不fork 5个仓库。

是否有其他方法来解决这个问题?我能否以某种方式指定要使用哪个 name?是否有办法通过告诉JVM要执行什么操作来消除这种歧义?

或者是不是没有办法可做,将枚举字段命名为 name 是使其在Kotlin中无法使用的有效方法?

英文:

I wrote a small program in Kotlin which uses library https://github.com/KaptainWutax/SeedUtils, specifically the Dimension enum https://github.com/KaptainWutax/SeedUtils/blob/master/src/main/java/kaptainwutax/seedutils/mc/Dimension.java

When I call e.g. Dimension.OVERWORLD.name, I get Overload resolution ambiguity.
I know what is the issue, the problem is that enum Dimension has name field, and the enum class itself has name field https://kotlinlang.org/docs/reference/enum-classes.html

The question is, what can I do about it. My current approach is to fork this library and all other 4 libraries I use and depend on it, renaming name in that enum to something else, but I hate having to fork 5 repos to rename single field.

Is there any other way to get around this? Can I specify somehow which name should be use? Is there a way to remove this ambiguity somehow by telling the JVM what to do?

Or is there nothing to be done and naming enum field name is effective way to make it unusable by Kotlin?

答案1

得分: 3

一个解决方法是在Java中编写一个辅助方法,以确保其不会引起歧义:

public class DimensionHelper {
    public static String getName(Dimension dimension) {
        return dimension.name;
    }
}

然后在Kotlin中,你可以在需要访问Dimension名称时调用 DimensionHelper.getName()。然后你可以在Kotlin中添加一个扩展方法:

fun Dimension.getName() = DimensionHelper.getName(this);

... 这将允许你只使用 Dimension.OVERWORLD.getName()

虽然远非理想,但确实避免了需要分叉。

(当然,完全可能存在我不知道的适用于Kotlin的特定方法。)

英文:

One workaround would be to write a helper method in Java, where it's unambiguous:

public class DimensionHelper {
    public static String getName(Dimension dimension) {
        return dimension.name;
    }
}

Then from Kotlin, you could call DimensionHelper.getName() whenever you wanted to access the name of a Dimension. You can then add an extension method in Kotlin:

fun Dimension.getName() = DimensionHelper.getName(this);

... which will allow you to just use Dimension.OVERWORLD.getName().

It's far from ideal, but it does avoid having to fork.

(It's entirely possible that there's a Kotlin-specific way of doing this that I'm unaware of, of course.)

答案2

得分: 1

除了@JonSkeet提供的解决方法之外,可能还有其他一些方法。我能想到的一个方法就是使用普通的Java反射API和Kotlin扩展方法:

private val nameField = Dimension::class.java.getDeclaredField("name")
fun Dimension.getName() = nameField.get(this) as String

这使你可以像这样调用它:

val name = Dimension.OVERWORLD.getName()

不幸的是,这种方法在使用Kotlin反射时不起作用,因为我们会遇到相同的错误:

Dimension::name // 重载决议模糊

这种使用反射的方法也可以通过只查找一次名称来进行改进。为此,创建一个包含Map<Dimension, String>的小助手对象:

private object NameHolder {
    val dimensionToName = EnumMap<Dimension, String>(Dimension::class.java)
    init {
        val nameField = Dimension::class.java.getDeclaredField("name")
        for (dimension in Dimension.values()) {
            dimensionToName[dimension] = nameField.get(dimension) as String
        }
    }
}

然后将你的扩展方法更改为:

fun Dimension.getName() = NameHolder.dimensionToName[this]!!
英文:

Next to the workaround provided by @JonSkeet there's possibly some other ways. The one I can think of is simply using the plain old java reflection api and a kotlin extension method:

private val nameField = Dimension::class.java.getDeclaredField(&quot;name&quot;)
fun Dimension.getName() = nameField.get(this) as String

Which allows you to call it like this:

val name = Dimension.OVERWORLD.getName()

Sadly this doesn't work with kotlin reflection, because then we'd run into the same error:

Dimension::name // Overload resolution ambiguity

This method using reflection can also be refined, by only looking the name up once. For that create a small helper object, containing a Map&lt;Dimension, String&gt;:

private object NameHolder {
    val dimensionToName = EnumMap&lt;Dimension, String&gt;(Dimension::class.java)
    init {
        val nameField = Dimension::class.java.getDeclaredField(&quot;name&quot;)
        for (dimension in Dimension.values()) {
            dimensionToName[dimension] = nameField.get(dimension) as String
        }
    }
}

and then change your extension method to this:

fun Dimension.getName() = NameHolder.dimensionToName[this]!!

huangapple
  • 本文由 发表于 2020年9月4日 15:01:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/63736339.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定