When creating a private companion object in Kotlin, why is the Companion static variable marked as #Deprecated public in the bytecode? Is that just a workaround to "simulate" a private behaviour discouraging developers from using that field (since a public companion object doesn't get marked as deprecated)?
Example:
Kotlin
class MyClassWithCompanion {
private companion object {
private val FOO = "FOO"
}
}
Bytecode
// DEPRECATED
// access flags 0x20019
public final static LMyClassWithCompanion$Companion; Companion
#Ljava/lang/Deprecated;()
The #Deprecated annotation is placed as an intermediate solution to avoid breaking binary compatibility when a compiler bug was fixed. The field wasn't supposed to be generated as public when the companion object is private, but due to an oversight it was. In Kotlin 1.4, it will be marked as private.
See this issue for more information.
Related
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.
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.
I have a class which impliments both the java.io.Serializable and android.os.Parcelable.
These classes require companion objects of:
companion object CREATOR : Parcelable.Creator<MyClass> {
override fun createFromParcel(parcel: Parcel): MyClass
...
}
and
companion object {
private val serialVersionUid: Long = 123
}
The trouble is that I can't have both these companion objects because that causes a only one companion object per class exception.
How can I have two companion objects with different names in the same class?
May be you misunderstood Java examples.
public static Parcelable.Creator<SDFileDir> CREATOR = ...;
public static long serialVersionUid = 123;
In Java - yes, it is separated static object. You can place any count of static fields in class.
In Kotlin there should be only one static object (it is called Companion here). But it is like one more class here. So all new static fields should be inside of it.
companion object {
#JvmField
val CREATOR: Parcelable.Creator<SDFileDir> = ...
val serialVersionUid: Long = 123
}
There is one more thing: annotation #JvmField to work with Java correctly.
I can suggest two solutions to this problem:
As #Ircover said - You can declare the CREATOR (which is simply a static field in Java) inside your companion object alongside your constants, but you'll need to mark in with #JvmField annotation to work as inteded (as it is called from Java)..
You do not necessarily need the companion object for the constant value, it (it won't work with serialVersionUid in your case, as it MUST be inside the class for Java serialization to work) can be moved to a separate object, to a companion object of another class or even inside any .kt file body (outside the class)..
In fact, companion object in kotlin doesn't correspond to static object in Java, they merely share similar funtionality.
In Java, there are only two concepts involved: the class and its static object.
In Koltin, we are dealing with three concepts: the class, the companion object, and the property of the companion object.
The way we access the property of the companion object is the same as accessing the static object in Java, but in Kotlin, there is an extra layer between the class and the inner property, that is the companion object.
In your case, you are not demanding two companion objects, but two properties of one companion object, so just place these two properties in one companion 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.
Official Ref says the default Visibility Modifier is public.
But from koan data classes it can be inferred that it is private.
Is it a contradiction? What is the default Visibility Modifier of property in kotlin?
---------The above is the initial question---------------
I didn't mix property and field up. But I did have confusion on how property is accessed.
To resolve my confusion, I actually should have asked the new question in edited title.
Self-answered below.
The default visibility for properties (and functions and classes, and...) is indeed public.
The Koan is a little confusing, because the Java class has private fields with public getters.
A property can be seen as the combination of field, a getter and an optional setter. The property in the Koan has a private backing field and a public getter.
If you for example write
val age = person.age
then Kotlin will basically generate a getAge() method of the Person class, that will be called internally. This method returns the field itself.
It's also possible to add behavior to that getter. You can find more info in that in the documentation.
It's therefore not a contradiction, but a different concept.
What it the default Visibility Modifier for properties in kotlin?
It is public, as the docs say
Why are the fields not private
In this example the fields are immutable, so there are no setters defined automatically. The data class automatically has getters, and uses them, but it simplifies reading the code by not requiring them to be manually called.
Worked example
This code:
object X {
data class Example(val a: String, val b: String)
#JvmStatic
fun main(args: Array<String>) {
val e = Example("a", "b")
println(e.a)
println(e.b)
}
}
The main method of this compiles to this (with checks and metadata removed):
public static final void main(String[] args) {
X.Example e = new X.Example("a", "b");
String var2 = e.getA();
System.out.println(e.getA());
var2 = e.getB();
System.out.println(var2);
}
(Decompiled using IntelliJ IDEA)
Property encapsulates backing field by defintion. Backing field is directly assigned only when being initialized. All accesses except initialization are done through accessors.
So the private modifier on field in Java is no longer needed in Kotlin.
And the public on getter and setter in Java is placed on property(actually, still for getter and setter) in Kotlin.
Therefore the omitted modifier is public and not private.