简体   繁体   中英

Inherited platform declarations clash when extending a Java class in Kotlin

I am trying to extend a third-party Java class with Kotlin but I get this compiler message:

Inherited platform declarations clash: The following declarations have the same JVM signature (setCollection(Ljava/util/Collection;)V):

  • fun setCollection(collection:(Mutable)Collection<(raw)Any?>!: Unit defined in KotlinClass

  • fun setCollection(collection:(Mutable)Collection<String!>!): Unit defined in KotlinClass

Whatever I do there is no way to get it compiled with Kotlin code only.

The code which reproduces the situation:

//JavaInterface.class
import java.util.Collection;

public interface JavaInterface {
    void setCollection(Collection<String> collection);
}

//JavaBaseClass.class
import java.util.Collection;

public class JavaBaseClass {
    public void setCollection(Collection collection){}
}

//JavaSubClass.class
public class JavaSubClass extends JavaBaseClass implements JavaInterface {}

//KotlinClass.kt
class KotlinClass : JavaSubClass() 

Java itself does not have this problem. So my guess is that this could be related to platform types ( String! is not Any? ).

Is there an elegant workaround to this problem which preferably doesn't involve writing Java code? Or should this be fixed within the Kotlin compiler itself?

I have not looked into the details of how/why the java works in your JavaSubClass, ie what bytecode it results in, but from the Kotlin perspective if I look at the signatures of those two methods, they would have ambiguous dispatch.

This:

public void setCollection(Collection collection){}

Is seen as a method setting a Collection with generic type Any?. Any? is a superset of String.

If a client of your class has the following:

myCollection = Array<String>()
myInstanceOfKotlinClass.setCollection(myCollection)

Which method should be dispatched?

For this reason, I think that this is not something that is a bug in the Kotlin compiler, but is an improvement on some of the looser java generic type safety. Obviously java makes a choice about how both of these methods compile into the same class in bytecode, and that is an okay approach if you know which method will be selected for dispatch in the above case. But I think that you should be able to tell which one from the signature alone, not some internal knowledge.

I am afraid I am struggling to find a workaround for it that would give you an extended kotlin class of that type. So if you are wanting to add more functionality to that java class, and you do not need any extra instance state, you could try and use extension functions?

fun JavaSubClass.newFunctionality(){}

It feels like the third party code you are using have themselves done a poor job of using generics and inheritance though. Either way though, if you need to change behaviour of existing functionality or have instance state for new functionality I think you are stuck with dealing with that ambiguity in Java land, because Kotlin disagrees with it as a matter of principal I think.

Edit:

Looking at the java, this

//JavaSubClass.class
public class JavaSubClass extends JavaBaseClass implements JavaInterface {}

Implies that JavaBaseClass implements the interface....

It has broadened the Generic type of the interface. Nasty type erasure bugs imminent. I mean look at this:

import java.util.ArrayList;

public class Main {

    public static void main(String[] args) {
        JavaSubClass sc = new JavaSubClass();
        ArrayList<Integer> myCollection = new ArrayList<>();
        sc.setCollection(myCollection);
    }
}

Eeew.

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