简体   繁体   中英

Overload resolution ambiguity in third party java library called from 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 eg 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?

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

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("name")
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<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
        }
    }
}

and then change your extension method to this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM