What's different between two constructors? - kotlin

What different between this type of constructor?
class ColorsArray(context: Context) {}
and
class ColorsArray(var context: Context){}

The second class not only declares a constructor that takes a Context, but it also has a property named context where it saves the value passed into the constructor. You can then access this like so:
val colorsArray = ColorsArray(context)
println(colorsArray.context)
Since you've declared it as a var and not a val, this can also be reassigned.
colorsArray.context = someOtherContext
Properties declared in the primary constructor are covered in the docs here.

In the first case constructor takes a context parameter and does nothing with it.
In the second case the code example provided:
class ColorsArray(var context: Context) { }
is a shortcut for:
class ColorsArray(context: Context) {
var context: Context = context
}

Related

Subclass Parameter Name Overriding Superclass Val

Experienced with Java, but fairly new to Kotlin.
When the subclass param has same name as a superclass val... Android Studio does not throw validation error stating need for #Override annotation. However, attempting to access name from within Business references the param name rather than the superclass val (which feels like an override to me).
class Business(
val name: String
) {
// ...
}
class FirstBusiness(name: String) : Business(name) {
val test = name; // name referencing param name rather than super's name
}
Of course, I can just name the param something different, but I really just want to pass the name to the superclass... otherwise excluding any storage of it in FirstBusiness.
Am I overlooking something? I'm surprised that even if I don't declare FirstBusiness param name as a val/var, it seems to be overriding Business.name. I'm assuming the param isn't truly overriding the super val as the IDE isn't complaining... but why is the param the only suggestion instead of the super val?
Edit: I do notice different (more expected from my Java experience) behavior if I do the param-passing outside of the primary constructor design like so...
class FirstBusiness : Business {
constructor(name: String) : super(name)
fun thing() {
val v = name // now references super's name
}
}
Thank you!
Just like how you would do it in Java if you have shadowed the name of a superclass's field, you can clarify it with the super keyword.
class FirstBusiness(name: String) : Business(name) {
val test = super.name
}
In your case, it's not overriding the superclass's property. What's happening is that property initializers at the property declaration sites are considered part of the primary constructor's initialization block, so the constructor parameter is closer in scope than the superclass's property.
Suppose for a moment that these classes were defined in Java, and in the superclass you simply used a field instead of a getter:
public class Business {
public String name;
public Business(String name) {
this.name = name;
}
}
Then your code where you initialize your property at its declaration site is just like initializing a field from a constructor, like this in Java:
public class FirstBusiness extends Business {
private String test;
public FirstBusiness(String name) {
super(name);
this.test = name; // It's using the parameter, not the superclass's
// property, but the superclass property isn't overridden.
}
}

How to write getters in Kotlin

I know a little java and am currently studying kotlin. I can't quite figure out getters. I have a class and some function.
class Client(val personalInfo: PersonalInfo?){} //class
fun sendMessageToClient(client: Client?) {
val personalInfo: PersonalInfo? = client?.personalInfo
//...
}
As far as I understand, getter is called in the code client?.personalInfo. Or is it a class field, since private is not explicitly specified anywhere?
Next, I want to add some logic to getter, but I get an error that such a signature already exists.
class Client(val personalInfo: PersonalInfo?){
fun getPersonalInfo():PersonalInfo?{
print(personalInfo)
return personalInfo
}
}
If I specify that the field is private, the error disappears class Client(private val personalInfo: PersonalInfo?), but but the code client?.personalInfowill not work
I tried to rewrite the code, but I can't figure out how to specify val and pass it a value from the constructor
class Client(personalInfo: PersonalInfo?) {
val personalInfo = //??
get() {
print("personal info $personalInfo")
return personalInfo
}
}
Is it possible to somehow add print to the getter and still use client?.personalInfo?
You were almost there. When creating custom getters in kotlin you must use the keyword field when you want the value of the associated property to be used (you can read more about this in re reference documentation at https://kotlinlang.org/docs/properties.html#backing-fields or at https://www.baeldung.com/kotlin/getters-setters#1-accessing-the-backing-field):
Every property we define is backed by a field that can only be accessed within its get() and set() methods using the special field keyword. The field keyword is used to access or modify the property’s value. This allows us to define custom logic within the get() and set() methods.
Having written this you just need to change your code a little bit as follows:
class Client(personalInfo: String?) {
val personalInfo: String? = personalInfo
get() {
print("personal info $field")
return field
}
}

::property.isInitialized cannot differentiate between method and property with same name

I'm creating a builder (for Java compat), where context is both a private property and public method.
private lateinit var context: Context
fun context(appContext: Context) = apply {
context = appContext
}
fun build(): MySdk {
// this::context fails to compile because it cannot differentiate between the
// method `context()` vs property `context`
require(this::context.isInitialized) {
"context == null"
}
But I get a compilation issue for ::context.isInitialized, because it cannot differentiate between the method context() vs property context
Does Kotlin have a workaround for this? or am I forced to use unique property/method names?
This is a case of overload resolution ambiguity and the kotlin compiler is unable to identify whether you are using the property or the method.
This is because of callable references (::) . Internally when you are using the callable references it calls a method.
Callable references : References to functions, properties, and
constructors, apart from introspecting the program structure, can also
be called or used as instances of function types.
The common supertype for all callable references is KCallable, where R is the return value type, which is the property type for properties, and the constructed type for constructors.
KCallable<out R> // supertype for all callable references
So, for function the type is KFunction and for properties the type is KProperty
interface KFunction<out R> : KCallable<R>, Function<R> (source)
interface KProperty<out R> : KCallable<R> (source)
When you use a function like :
fun context(appContext: Context) = apply {
context = appContext
}
It can be used as a Function reference
::context // This is a Function reference i.e. KFunction
When you use a property reference, like
private lateinit var context: Context
fun something(){
::context // this is a property reference, KProperty
}
A property reference can be used where a function with one parameter is expected:
val strs = listOf("a", "bc", "def")
println(strs.map(String::length))
So, its not that Kotlin forces you to use different property and function names("although it is not recommended"). Its just that its unable to differentiate in this case as
Both are KCallable and have the same name
A property reference can be used where a function with one parameter is expected
You can resolve the ambiguity between the property and the method by giving the expected type:
val prop: kotlin.reflect.KProperty0<*> = this::context
Alas, prop.isInitialized then gives a compilation error:
This declaration can only be called on a property literal (e.g. 'Foo::bar')
So this doesn't appear to be possible currently. OTOH, since the error shows isInitialized is already handled specially by the compiler, it's likely possible to fix; I suggest reporting it on http://youtrack.jetbrains.com/ (after searching for duplicates).

Calling a class's method as default arg in constructor

I'm constructing a class and then trying to call a member method of that class as a default value for one of the constructor args.
Why isn't this valid Kotlin?
// unresolved reference: defaultText
class MyThing(val text: String = defaultText()) {
fun defaultText() = "hi"
}
It's possible using two separate constructors in both Java and Kotlin, but then I lose the conciseness of default args.
class MyThing {
private val text: String
constructor(text: String) {
this.text = text
}
constructor() {
this.text = defaultText()
}
private fun defaultText(): String {
return "hi"
}
}
The biggest problem of having a constructor's default parameter expression call a member function of the same instance is that the default arguments are evaluated before the constructor is called.
Given that, such a member function would have to run on a completely un-initialized instance of the class (because even the super constructors will work after that, see this answer about the execution order).
Usually, member functions perform some logic taking the instance state into account, and having a member function run on an empty instance might break some of that logic (e.g. all fields will hold nulls, even the backing fields of Kotlin not-null properties). Overall, even when such calls do not fail at runtime, they are likely introduce subtle bugs, so using a completely uninitialized instance is prohibited.
With regard to the secondary constructor, well, at least it runs after the super constructor initializes some part of the instance, which is thus not completely empty, but it's up to you to make sure you don't use the parts of the class that are not initialized (if you do, you may again encounter a runtime failure or introduce a bug).
I'd rather suggest using a function of a companion object (those are initialized before the class is first used) for this purpose:
class MyThing(val text: String = defaultText()) {
companion object {
fun defaultText() = "hi"
}
}
Or even a top-level function:
fun defaultText() = "hi"
class MyThing(val text: String = defaultText())

How are overridden properties handled in init blocks?

I'm trying to understand why the following code throws:
open class Base(open val input: String) {
lateinit var derived: String
init {
derived = input.toUpperCase() // throws!
}
}
class Sub(override val input: String) : Base(input)
When invoking this code like this:
println(Sub("test").derived)
it throws an exception, because at the time toUpperCase is called, input resolves to null. I find this counter intuitive: I pass a non-null value to the primary constructor, yet in the init block of the super class it resolves to null?
I think I have a vague idea of what might be going on: since input serves both as a constructor argument as well as a property, the assignment internally calls this.input, but this isn't fully initialized yet. It's really odd: in the IntelliJ debugger, input resolves normally (to the value "test"), but as soon as I invoke the expression evaluation window and inspect input manually, it's suddenly null.
Assuming this is expected behavior, what do you recommend to do instead, i.e. when one needs to initialize fields derived from properties of the same class?
UPDATE:
I've posted two even more concise code snippets that illustrate where the confusion stems from:
https://gist.github.com/mttkay/9fbb0ddf72f471465afc
https://gist.github.com/mttkay/5dc9bde1006b70e1e8ba
The original example is equivalent to the following Java program:
class Base {
private String input;
private String derived;
Base(String input) {
this.input = input;
this.derived = getInput().toUpperCase(); // Initializes derived by calling an overridden method
}
public String getInput() {
return input;
}
}
class Derived extends Base {
private String input;
public Derived(String input) {
super(input); // Calls the superclass constructor, which tries to initialize derived
this.input = input; // Initializes the subclass field
}
#Override
public String getInput() {
return input; // Returns the value of the subclass field
}
}
The getInput() method is overridden in the Sub class, so the code calls Sub.getInput(). At this time, the constructor of the Sub class has not executed, so the backing field holding the value of Sub.input is still null. This is not a bug in Kotlin; you can easily run into the same problem in pure Java code.
The fix is to not override the property. (I've seen your comment, but this doesn't really explain why you think you need to override it.)
The confusion comes from the fact that you created two storages for the input value (fields in JVM). One is in base class, one in derived. When you are reading input value in base class, it calls virtual getInput method under the hood. getInput is overridden in derived class to return its own stored value, which is not initialised before base constructor is called. This is typical "virtual call in constructor" problem.
If you change derived class to actually use property of super type, everything is fine again.
class Sub(input: String) : Base(input) {
override val input : String
get() = super.input
}