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

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.

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):
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.
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...
// ...
}
}

You can use javap to analyze the problem, showing members of compiled interface and classes.
javap Base
public interface Base<T> {
public abstract void go(T);
}
javap Impl
public abstract class Impl implements Base<java.lang.Integer> {
public void go(int);
public void go(java.lang.Object);
public Impl();
}
So, the problem is exactly that pointed out by #Roland: in order to satisfay the contract requested by the Base interface, the Java compiler needs a public void go(java.lang.Integer) method but the method generated by Kotlin compiler has int as parameter.
If you implement the interface in Java, with something like
class JI implements Base<Integer> {
#Override
public void go(#NotNull Integer field) {
}
}
you can analyze its compiled version with javap obtaining
javap JI
class JI implements Base<java.lang.Integer> {
org.amicofragile.learning.kt.JI();
public void go(java.lang.Integer);
public void go(java.lang.Object);
}
So, if you plan to use Kotlin class Impl as superclass of Java classes, the solution is simply to use <Integer>, not <Int>, as type parameter: Int is a Kotlin class, translated to int by the compiler; Integer is the Java class you usually use in Java code.
Changing your example to
abstract class Impl : Base<Integer> {
override fun go(field: Integer) {}
}
public class JavaImpl extends Impl {
}
the JavaImpl Java class compiles without errors.

Related

Remove $init$ method from class with subclass

I am using a Java library in Scala that generates D-Bus interfaces from class definitions. It works fine with a simple Scala class:
#DBusInterfaceName("me.TestInterface")
trait TestInterface extends DBusInterface {
def changeSomething(thing: String): Unit
}
I need to add a sub-class in order to implement a D-Bus Signal:
#DBusInterfaceName("me.TestInterface")
trait TestInterface extends DBusInterface {
def changeSomething(thing: String): Unit
final class SomethingChanged(thing: String) extends DBusSignal("SomethingChanged")
}
When I do this I get an extrat method $init$ on my D-Bus interface, presumably because this is being added by Scala.
Is it possible to generate a class with a sub-class in Scala that does not have this extra $init$ method?
I can clearly just write this in Java and import it, but I would rather stick to pure Scala.
I found a solution. I realised that Java uses a static class as the inner class. The equivalent in Scala is to put the class in the companion object:
#DBusInterfaceName("me.TestInterface")
trait TestInterface extends DBusInterface {
def changeSomething(thing: String): Unit
}
object TestInterface {
case class SomethingChanged(thing: String) extends DBusSignal("/me/Test")
}
This does work in Scala 2.13, but I am not sure whether this is behaviour that I can rely on or just a feature of the current implementation.

Kotlin Error : 'public' function exposes its 'public/*package*/' return type argument

I am new to Kotlin and trying out to write some project using the language.
I am using Java library and extending a class from the library in my project and I am seeing this error message.
'public' function exposes its 'public/*package*/' return type argument FooSettings
I understand the problem is but I am not sure how to fix it in Kotlin since I am still trying get familiar with Kotlin.
I can see that Kotlin is being smart and only trying to return of type that extends FooSettings. However the problem is FooSettings is package public only which means that I cannot access if in my Kotlin project.
I did some research about Kotlin generics and use of in or out but I wasn't able to fix the problem.
Is there any work around that I can do in my Kotlin project to fix the error I am seeing?
Code snippet
This is sample of Java library class:
Note, I have no way to changing the implementation of the library. I must use this Library and extend it in Kotlin.
It seems odd to me that the java library is written such a way and expect it to be overridden but that is question for another day.
import java.util.Collections;
import java.util.List;
public abstract class ClassA {
public List<FooBuilder<?>> getBuilder(Foo foo) {
return Collections.emptyList();
}
}
public class Foo {
}
public abstract class FooBuilder<U extends FooBuilder.FooSettings> {
// implementation of Class
abstract static class FooSettings {
// implementation of Class
}
}
Normally Java classes would override the method like such:
import java.util.List;
public class MyJavaClassA extends ClassA {
#Override public List<FooBuilder<?>> getBuilder(final Foo foo) {
// implementation
}
}
But I am trying to write in Kotlin such that it looks like: Reminder that this Kotlin is depending on the Java library and does not have access to package public classes.
class MyKotlinClassA : ClassA() {
override fun getBuilder(foo: Foo): MutableList<FooBuilder<*>> {
// implementation
}
}
This causes error
'public' function exposes its 'public/*package*/' return type argument FooSettings
I presume that by "package public" you meant "package private"? In your example, FooBuilder.FooSettings has no visibility modifier so uses the Java default of package private. Assuming that's what you meant...
You will be able to access the package private class, FooSettings, in your Kotlin code, but only if you put that Kotlin code in a package matching the one where FooSettings is declared.
You'll still get the same compilation error, but that's not because you can't access the type: it's because you're trying to use it in a context which is more visible than the type's declaration. i.e. you're trying to take a package private type and use it as part of a public method's signature, which isn't allowed. To get round that problem you need to mark your Kotlin class as internal.
It's might also be worth mentioning that internal for Kotlin means it's visible in that module, not in that package. This is all explained in more detail here.
In my case, I was getting this error because I was importing a kotlin class variable from another java file which raised because of the auto conversion from java to kotlin by Android Studio.
I was able to fix it by changing all the references of the variable in the java file to its setters and getters.
eg:
// kotlin file
internal open class BubbleBaseLayout : FrameLayout {
var windowManager: WindowManager? = null
lateinit var viewParams: WindowManager.LayoutParams
// defined here
var layoutCoordinator: BubblesLayoutCoordinator? = null
// ...
}
// Java file
// This variable
if (layoutCoordinator != null) { ... }
Needs to be changed to
// layoutCoordinator to getlayoutCoordinator everywhere
if(getlayoutCoordinator() != null){ ... }

How do I correctly generate a dynamic proxy class that's based on the right class?

I have an interface defined as follows:
public interface Cache {
}
Then an abstract class implementing the above:
public abstract class AbstractCache implements Cache {
}
Then a concrete class inheriting from above:
public class RealTimeCache extends AbstractCache {
}
Then another class defined as follows:
public class CacheProbe {
public static <T> T probe(T base) {
return (T) Proxy.newProxyInstance(
base.getClass().getClassLoader(),
new Class[]{Cache.class},
new MethodCountInvocationHandler(base) // I am not mentioning this class as it's irrelevant
);
}
}
I have a class as follows which is using all of the above:
public class CacheLoader<T extends Cache> {
public T load() {
T result = getResult(...);
CacheProbe x = new CacheProbe(result);
return x.probe();
}
}
Lastly, the lines causing the issue (located outside above classes):
final CacheLoader<RealTimeCache> cacheLoader = getNewLoader(); //Method of this method is irrelevant and unchangeable
RealTimeCache x = cacheLoader.load(); //This is the line which is causing a runtime issue
Problem is, at run time the following exception is thrown at the last line mentioned above:
java.lang.ClassCastException: com.sun.proxy.$Proxy57 cannot be cast to RealTimeCache
However I don't see how this is possible because the dynamic proxy class generated is based on Cache.
How do I fix this ?
Please note that I can only change CacheProbe class in order to fix this. Cache, AbstractCache, RealTimeCache, CacheLoader and those last two lines are unchangeable.
However I don't see how this is possible because the dynamic proxy class generated is based on Cache.
Yes, the docs for java.lang.reflect.Proxy say
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.
(emphasis added)
Thus, you cannot use Proxy to create (an instance of) a subclass of an arbitrary class of your choice.
How do I fix this ?
You can create an ordinary subclass of RealTimeCache, and return an instance of that. Proxy is meant primarily to serve for interfaces that are not known until runtime, and in that case the only way to interact with them anyway is the interface type. That's not your scenario.
If necessary, you can implement such a subclass in terms of a MethodCountInvocationHandler, just as your proxy class uses, but I'm sure it would be easier to implement whatever tooling that is supposed to provide directly.

explicitly override a Java method in Jruby?

Is there an explicit way to override a Java method in JRuby-subclass?
public class Yours {
public String hi() {
return "Hello original";
}
}
In a Java I'd use #override to make subclassing explicit.
public class Mine extends Yours {
#Override // throws an error if the above is not a superclass method
public String hi() {
return "Hello override!";
}
}
When I override this in Jruby, I'd like something like this:
class JRMine < Yours
java_overrides # I wish this was there, making sure "wiring" is ok
def hi()
"Hello Jruby"
end
end
Now, is there any equivalent technique to achieve safe overriding?
It seems it could avoid some hard-to-track errors in java integration, due to just relying on method naming.
(Actually I find it would be handy in Ruby generally too, to a lesser extent..)
UPDATE: now packed into gem 'overrides' https://github.com/kares/overrides
with a bit of meta-programming this is possible to do with Ruby methods (and works with JRuby since Java inherited methods show up as Ruby ones) ... I've put it up in a gist :
https://gist.github.com/kares/7434811 ... now that someone finds it useful might put it in a gem :)
usage sample (NOTE: you do not need to hook it up for all classes/modules) JRuby style :
Object.extend Override
class JList < java.util.ArrayList
override
def trim_to_size; super; end
def isEmpty; false; end
override :isEmpty
end

type definition in a package object "hijacking" the inheritance of a java class in scala code

I have the following situation:
I have a Java class hierarchy like this:
package org.foo.some;
public class Model extends org.foo.some.GenericModel { // ... }
package org.bar;
public class MyModel extends org.foo.some.Model { // ... }
where org.foo.some.Model and org.foo.some.GenericModel are out of my reach (not my code). In Scala, also out of my reach, there is:
package org {
package foo {
package object some {
type Model = org.foo.some.ScalaModel
}
}
}
This leads to a funny behavior in Scala code, e.g.
val javaModel:MyModel = new org.bar.MyModel()
trait FooTrait[T <: org.foo.some.GenericModel] { // ... }
class FooClass extends FooTrait[MyModel] { //... }
does not compile and raises the following error:
type arguments [org.bar.MyModel] do not conform to trait FooTrait's type
parameter bounds [T <: org.foo.some.GenericModel]
Further, I can't invoke any method of org.foo.some.Model nor of org.foo.some.GenericModel on javaModel:
javaModel.doSomething()
raises
value create is not a member of org.bar.MyModel
I am under the impression that the package object is "hijacking" the visibility of the Java class hierarchy in Scala code. Indeed, ScalaModel does not extend org.foo.some.GenericModel.
Is there maybe a way to still access the hierarchy from within Scala code?
Edit: when re-compiling the code out of my reach and removing the type re-definition, everything works. So I think what I'm looking at is a way to "disable" an package-level type definition for a specific class.
Are you using a GUI (in particular Eclipse) to build your project?
This seems related to Scala trouble accessing Java methods (that has no answer but where the general consensus is that the problem is not with scala but with Eclipse).

Categories