A note for people using Mixins which implement interfaces which have default implementations of methods which are written in Kotlin.
Lets say you have an interface in Kotlin:
interface MyKotlinterface {
fun doMyThing() {
x.y.z.doThing(this)
}
}
Enter fullscreen mode Exit fullscreen mode
and a mixin:
@Mixin(PlayerEntity.class)
abstract class MyMixin implements MyKotlinterface {}
Enter fullscreen mode Exit fullscreen mode
you might think that you can then do (player as MyKotlinterface).doMyThing()
. However, you will get an AbstractMethodError
. Why? because Kotlin doesn’t compile your interface how you’d expect it to.
You’d think that it would compile like:
public interface MyKotlinterface {
public default void doMyThing() {
x.y.z.doThing(this)
}
}
Enter fullscreen mode Exit fullscreen mode
but that would only work on JVMs at or above version 8, when default implementations for methods in interfaces were added – so Kotlin compiles it like this instead, which works on all JVMs:
public interface TestInterface {
/* All calls to the default impl of this method * are rewritten by the kotlin compiler to use * the static method */
public void doMyThing();
public static final class DefaultImpls {
public static void doMyThing(TestInterface obj) {
x.y.z.doThing(obj)
}
}
}
Enter fullscreen mode Exit fullscreen mode
The side-effect of this is that when you add your interface to a class with that mixin, it bypasses the kotlin compiler’s rewriting – and so, attempts to access the default impl of the method on the player will crash with an AbstractMethodError
. There are different fixes for this, depending on which Kotlin version you’re using:
- For Kotlin <1.2, you’ll have to just write your implementations in your mixin class.
- For Kotlin 1.2<=version<1.4, annotate your method with
@JvmDefault
, and put this in your build.gradle:
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
freeCompilerArgs += '-Xjvm-default'
}
}
Enter fullscreen mode Exit fullscreen mode
- For Kotlin >=1.4, just put this in your build.gradle:
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
kotlinOptions {
freeCompilerArgs += '-Xjvm-default=all'
}
}
Enter fullscreen mode Exit fullscreen mode
and your default methods will be compiled as they should be!
with thanks to this blog post and this decompiler/bytecode viewer
原文链接:Mixins, Kotlin, and Default Methods – why they don’t work together
暂无评论内容