Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extending Kotlin class by Java requires me to reimplement already implemented method

Tags:

java

kotlin

The simplest code to demonstrate the issue is this:

Main interface in Kotlin:

interface Base <T : Any> {
  fun go(field: T)
}

Abstract class implementing it and the method:

abstract class Impl : Base<Int> {
  override fun go(field: Int) {}
}

Java class:

public class JavaImpl extends Impl {
}

It should work, but it doesn't. The error is

Class 'JavaImpl' must either be declared abstract or implement abstract method 'go(T)' in 'Base'

If the JavaImpl class was in Kotlin, it would work. Also if the T was cast to String or Integer or any object, it would work too. But not with Int.

Is there any clever solution apart from using Integer and suppressing hundreds of warnings in Kotlin subclasses?

Update: created the issue.

like image 697
Erlik Avatar asked Sep 17 '19 14:09

Erlik


People also ask

How do I extend a Java class in Kotlin?

In Kotlin we use a single colon character ( : ) instead of the Java extends keyword to extend a class or implement an interface.

How do I extend my Kotlin class?

Kotlin provides the ability to extend a class or an interface with new functionality without having to inherit from the class or use design patterns such as Decorator. This is done via special declarations called extensions.

Can we use Kotlin class in Java?

Android Studio provides full support for Kotlin, enabling you to add Kotlin files to your existing project and convert Java language code to Kotlin. You can then use all of Android Studio's existing tools with your Kotlin code, including autocomplete, lint checking, refactoring, debugging, and more.


1 Answers

Looking at the byte code we can see, that the Impl-class basically has produced the following function:

public go(I)V

where the parameter is a primitive integer. Also a synthetic bridge-function (go(Object)) is generated, which however would also be generated on the Java-side for such generic functions.

On the Java side however it doesn't suffice to have something like public void go(int field) in place. Now we need that go(Integer field)-function, which isn't present. For me that sound's like an interop-problem that should probably be reported and linked back here again. Actually having had some time to investigate, there seem to be some issues already: KT-17159 and KT-30419, but also KT-5128 seem to relate to this problem. The kotlin compiler knows how to deal with this and doesn't need any further information about it in the class-file (i.e. the implementing Kotlin class knows, that it doesn't need to implement something like fun go(field : Int?)). For the Java-side such counterpart does not exist. I wonder whether this could even be fixed nicely with the compiler/byte-code or whether this will remain a specific interop-problem.

Some workarounds to deal with that problem (in case this is deliberate and not a real problem):

  1. Add an additional function as follows to Impl:

    fun go(field : Int?) = go(field ?: error("Actually the given field should never be null"))
    // or simply:
    fun go(field : Int?) = go(field!!)
    

    That way you would not need to implement it. However then you would also expose that nullable function to the Kotlin side, which you probably don't want.

  2. For that specific purpose it may seem more convenient to do it the other way around: declare the class and the interface in Java and use it on the Kotlin side. That way you could still declare something like

    abstract class KotlinClass : JavaInterface<Int> {
      override fun go(field : Int) { // an IDE might suggest you to use Int? here...
        // ...
      }
    }
    
like image 171
Roland Avatar answered Sep 19 '22 16:09

Roland