Protected inline method in parent class can't access other protected methods - kotlin

I am having a problem getting IllegalAccessError for the following example:
I have a base class declared in a gradle module called arch
abstract class BaseClass {
protected abstract val value: Int
fun run() {
Log.d("Printme", "value $value")
}
protected inline fun getMyValue(): Lazy<Int> = lazy {
getAnEight()
}
protected fun getAnEight() = 8
}
and a child class declared in gradle module called app
class ChildClass: BaseClass() {
override val value by getMyValue()
}
It is worth saying I am creating a Kotlin project using Android Studio, but these classes are all simple Kotlin objects without any Android specific references. Of course these two modules also have different packages.
Now, from my main entry method I am doing the following (inside app module)
ChildClass().run()
I am calling my run() method declared in base class, which is accessing lazy initiated value property, which is in turn calling getAnEight() method. Since all methods are protected I would expect there is no reason a child class can't call all these. Even if one of the methods is marked as inline and this call gets replaced with method contents, it should still be able to call getAnEight() just fine.
Instead I am receiving IllegalAccessError saying BaseClass.getAnEight() is inaccessible to class ChildClass$$special$$inlined$getMeValue$1. This problem disappears when I remove inline modifier, or if I place BaseClass in the same package as ChildClass.
Is this a bug in Kotlin compiler? Or can someone explain to me this behavior if it's working as intended? Thanks in advance!

https://kotlinlang.org/docs/reference/inline-functions.html#public-inline-restrictions
When an inline function is public or protected and is not a part of a
private or internal declaration, it is considered a module's public
API. It can be called in other modules and is inlined at such call
sites as well.
This imposes certain risks of binary incompatibility caused by changes
in the module that declares an inline function in case the calling
module is not re-compiled after the change.
To eliminate the risk of such incompatibility being introduced by a
change in non-public API of a module, the public API inline functions
are not allowed to use non-public-API declarations, i.e. private and
internal declarations and their parts, in their bodies.
An internal declaration can be annotated with #PublishedApi, which
allows its use in public API inline functions. When an internal inline
function is marked as #PublishedApi, its body is checked too, as if it
were public.
EDIT: I made some bytecode research. The problem is that protected getMyValue() function is inlined into public constructor. In decompiled bytecode, ChildClass public constructor has a following line:
Lazy var4 = LazyKt.lazy((Function0)(new ChildClass$$special$$inlined$getMyValue$1(this)));
As you can see, it creates an instance of class ChildClass$$special$$inlined$getMyValue$1. Let's look at its declaration:
public final class ChildClass$$special$$inlined$getMyValue$1 extends Lambda implements Function0 {
final BaseClass this$0;
public ChildClass$$special$$inlined$getMyValue$1(BaseClass var1) {
super(0);
this.this$0 = var1;
}
public Object invoke() {
return this.invoke();
}
public final int invoke() {
return this.this$0.getAnEight(); // Here lies the problem
}
}
When you create a ChildClass instance, its constructor only creates a ChildClass$$special$$inlined$getMyValue$1 instance, that does not throw any errors. But when you call run(), invoke() method of class above is called. This method is public, its class is public, constructor was public, but getAnEight method is protected. That's how we get this error.

Related

Add protected and internal visibility to function in kotlin

I have an open (or abstract) public class that contains an open (or abstract) fun (or var or val) that I do not want to expose as public.
Unfortunately I also need to call that fun from inside my current package.
If I mark it as internal that class cannot be inherited correctly from another package(ide gives warning: inherits invisible abstract members), if I mark it as protected the fun cannot be access from inside the current package
Any way to bypass this?
A dirty way is to add a proxy internal method and call that method inside your package:
abstract class AbstractClass {
protected abstract fun isTrue(int: Int): Boolean
internal fun isTrueInternalProxy(int: Int): Boolean {
return isTrue(int)
}
}

Implement setter from java interface as kotlin property

I have simple getter and setter for a boolean field in Java interface:
public interface Interface1 {
void setValue1(boolean value);
boolean getValue1();
}
When trying to implement that as a property in a class in Kotlin:
class Class1: Interface1 {
var value1 = false
}
I get the compilation error:
Class 'Class1' is not abstract and does not implement abstract member public abstract fun setValue1(value: Boolean): Unit defined in com.example.Interface1.
So only the getter is overridden. Is it possible to fix that without implementing the both getter and setter manually, without kotlin "sugar"?
https://youtrack.jetbrains.com/issue/KT-6653 says
This is a rather deep issue, unfortunately. It's unlikely that we'll ever make it work the way you'd like
and this stance doesn't seem to have changed since 2015.

Kotlin compiler issue with overriding of Java final function in Kotlin

I’m dealing with following issue with Kotlin/Java Compiler.
Imagine following scenario: let First be a Java class with a final function and Second be a Kotlin class extending First with a function of the same name like the final function in First class, example:
// Java class
class First {
final void foo() { }
}
// Kotlin class
class Second: First() {
fun foo() { }
}
Obviously, it’s wrong because the final function foo() can not be overridden. However, compilation pass successfully and in run-time I get java.lang.LinkageError: Method void Second.foo() overrides final method in class First.
Is this correct behavior of compiler? I supposed that there will be some validations for this case. Thank you!

Why private constructor of sealed class can be called in sub class?

Sealed class in Kotlin can have private constructor only. That means we can call the constructor only in itself:
Sealed classes are not allowed to have non-private constructors (their constructors are private by default).
// `private` and `constructor()` are redundant.
sealed class Expr private constructor()
But, when we utilize sealed class, a sub class have to inherit seald class:
// Above Kotlin 1.1
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
As you can see the code above, sealed class's private constructor is called outside of sealed class itself. When sub class is instantiated, the constructor of parent(sealed class) will be called before sub class's own constructor is called. Is it just exception to visibility modifiers?
https://kotlinlang.org/docs/reference/visibility-modifiers.html#classes-and-interfaces
For members declared inside a class: private means visible inside this class only (including all its members);
Consider the following code:
open class A private constructor(var name: String){
class B : A("B")
class C : A("C")
}
The above code compiles fine, as the constructor is called inside the class A.
If a class D tries to inherit outside A, it won't compile.
class D : A("D") // Error: Cannot access '<init>': it is private in 'A'
As mentioned on the page Sealed class in Kotlin,
A sealed class can have subclasses, but all of them must be declared in the same file as the sealed class itself. (Before Kotlin 1.1, the rules were even more strict: classes had to be nested inside the declaration of the sealed class).
It seems that kotlin relaxed the requirement of nested classes only.
So, the following code works fine in 1.1+ but would fail in earlier versions:
sealed class A(var name: String)
class B : A("B")
class C : A("C")
whereas the following code would have been required in versions before 1.1, which respects the private constructor.
sealed class A (var name: String){
class B : A("B")
class C : A("C")
}
So, allowing private constructors of sealed classes outside the class (but within the same file) can be considered an enhancement to make the code cleaner.
You can figure out what's happening by taking a look at the generated bytecode (you can do this by going to Tools -> Kotlin -> Show Kotlin Bytecode and then choosing Decompile in the pane that appears.). Decompiling it to Java shows this code for the Expr class:
public abstract class Expr {
private Expr() {
}
// $FF: synthetic method
public Expr(DefaultConstructorMarker $constructor_marker) {
this();
}
}
So there is a non-private constructor for the Expr class generated, with a special parameter. Then, as you'd expect, if you look at the decompiled bytecode of Const for example, you'll see that it calls into this constructor:
public final class Const extends Expr {
public Const(double number) {
super((DefaultConstructorMarker)null);
this.number = number;
}
// other fields and methods ...
}
You still can't subclass Expr from Kotlin, because the Kotlin compiler knows that it's a sealed class from the metadata in the file, and will respect that.
As for Java client code, there you can't access this same constructor yourself because the DefaultConstructorMarker is package-private in the kotlin.jvm.internal package that it's in, so even if you write out the import statement for it manually, the compiler won't allow it.
My guess is that the package-private visibility might only be enforced at compile time, and that's why the Kotlin compiler is able to output the bytecode corresponding to the snippet above (not completely sure though).

Is there a way to hide the INSTANCE variable on a Kotlin singleton object

If I have code like this
object ObjectTest {
#JvmStatic
fun init() {
}
}
is it possible to hide the ObjectTest.INSTANCE variable that Kotlin automatically generates? I don't want the object to be accessible via an instance and nor will it have any instance methods, so the INSTANCE variable is just polluting autocomplete and could be confusing to potential users (This code is for a library that will be consumed by others).
Yes, you can do it, by converting an object into a plain file.
#file:JvmName("ObjectTest")
// maybe a package statement here
fun init() {
// here `init` is public static final void
}
And there's no INSTANCE object. In Kotlin this is a top-level function, but in Java it's a class named ObjectTest with a private constructor and it has a public static final void method called init.