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.
}
}
Related
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
}
}
So I have a class with a generic type
class GenericClass<T> {
// At some point in the class I have variable item of type T
val name: String = item.name
}
I know for sure that the type T of GenericClass will be used with a class that has the "name" property. But of course at the line I got a "Unresolved reference name". Android Studio generated me this code via "Create extension property T.name"
private val <T> T.name: String
get() {}
I don't really know what to put in the bracket {} after the get. I tried return name but I got a recursive property error.
Any ideas ?
Thanks
If you know that every type T has property name you can declare it implicitly:
// GenericClass.kt
class GenericClass<T : HasName> {
// At some point in the class I have variable item of type T
val name: String = item.name
}
// HasName.kt
// Create new interface with "name" property
interface HasName {
val name: String
}
But also you must implement this new interface for all classes that can be used as T.
I know for sure that the type T of GenericClass will be used with a class that has the "name" property.
Then you need to explicitly declare that. By default, T extends Any?. You need to narrow down possible types of T by declaring some interface, like
interface Named {
val name : String
}
and passing T : Named as a generic paramteter. Also you need to make all classes, you're going to pass as a generic parameter, to implement that interface. By the way, GenericClass<T : Named> class itself could be declared as implementing that interface:
class GenericClass<T : Named> : Named {
override val name: String = item.name
}
So I have a Kotlin class that looks something like this:
class MyClass {
var myString: String = ""
set(value) {
field = value
doSomethingINeed()
}
constructor(myString: String) {
this.myString = myString
}
}
However, Android Studio is warning me that I can use this as a default constructor. When I select that, it changes it to this:
class MyClass(var myString: String)
Now I lose the opportunity to override the setter, because if I make a method called setMyString() I'll get a compiler error.
Is there a way to override the setter if the field is part of the default constructor, or do I have to go with option 1 and just ignore the warning that I get?
The quick fix for it definitely screws things up but the comment is trying to point you in the correct direction. You want to define a primary constructor that accepts just a parameter (not defining a property) and then use that parameter for the property initialization. Something like this:
class MyClass(myString: String) {
var myString: String = myString
set(value) {
field = value
doSomethingINeed()
}
}
While developing for android I sometimes come across something that looks like this:
var someModel: someViewModel by notNullAndObservable { vm ->
...
}
I don't understand what the significance of the by keyword is.
In simple words, you can understand by keyword as provided by.
From the perspective of property consumer, val is something that has getter (get) and var is something that has getter and setter (get, set). For each var property there is a default provider of get and set methods that we don't need to specify explicitly.
But, when using by keyword, you are stating that this getter/getter&setter is provided elsewhere (i.e. it's been delegated). It's provided by the function that comes after by.
So, instead of using this built-in get and set methods, you are delegating that job to some explicit function.
One very common example is the by lazy for lazy loading properties.
Also, if you are using dependency injection library like Koin, you'll see many properties defined like this:
var myRepository: MyRepository by inject() //inject is a function from Koin
In the class definition, it follows the same principle, it defines where some function is provided, but it can refer to any set of methods/properties, not just get and set.
class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface
This code is saying:
'I am class MyClass and I offer functions of interface SomeInterface which are provided by SomeImplementation.
I'll implement SomeOtherInterface by myself (that's implicit, so no by there).'
In the Kotlin reference you will find two uses for by, the first being Delegated Properties which is the use you have above:
There are certain common kinds of properties, that, though we can implement them manually every time we need them, would be very nice to implement once and for all, and put into a library. Examples include lazy properties: the value gets computed only upon first access,
observable properties: listeners get notified about changes to this property,
storing properties in a map, not in separate field each.
Here you delegate the getter/setter to another class that does the work and can contain common code. As another example, some of the dependency injectors for Kotlin support this model by delegating the getter to receiving a value from a registry of instances managed by the dependency injection engine.
And Interface/Class delegation is the other use:
The Delegation pattern has proven to be a good alternative to implementation inheritance, and Kotlin supports it natively requiring zero boilerplate code. A class Derived can inherit from an interface Base and delegate all of its public methods to a specified object
Here you can delegate an interface to another implementation so the implementing class only needs to override what it wants to change, while the rest of the methods delegate back to a fuller implementation.
A live example would be the Klutter Readonly/Immutable collections where they really just delegate the specific collection interface to another class and then override anything that needs to be different in the readonly implementation. Saving a lot of work not having to manually delegate all of the other methods.
Both of these are covered by the Kotlin language reference, start there for base topics of the language.
The syntax is:
val/var <property name>: <Type> by <expression>.
The expression after by is the delegate
if we try to access the value of property p, in other words, if we call get() method of property p, the getValue() method of Delegate instance is invoked.
If we try to set the value of property p, in other words, if we call set() method of property p, the setValue() method of Delegate instance is invoked.
Delegation for property:
import kotlin.reflect.KProperty
class Delegate {
// for get() method, ref - a reference to the object from
// which property is read. prop - property
operator fun getValue(ref: Any?, prop: KProperty<*>) = "textA"
// for set() method, 'v' stores the assigned value
operator fun setValue(ref: Any?, prop: KProperty<*>, v: String) {
println("value = $v")
}
}
object SampleBy {
var s: String by Delegate() // delegation for property
#JvmStatic fun main(args: Array<String>) {
println(s)
s = "textB"
}
}
Result:
textA
value = textB
Delegation for class:
interface BaseInterface {
val value: String
fun f()
}
class ClassA: BaseInterface {
override val value = "property from ClassA"
override fun f() { println("fun from ClassA") }
}
// The ClassB can implement the BaseInterface by delegating all public
// members from the ClassA.
class ClassB(classA: BaseInterface): BaseInterface by classA {}
object SampleBy {
#JvmStatic fun main(args: Array<String>) {
val classB = ClassB(ClassA())
println(classB.value)
classB.f()
}
}
Result:
property from ClassA
fun from ClassA
Delegation for parameters:
// for val properties Map is used; for var MutableMap is used
class User(mapA: Map<String, Any?>, mapB: MutableMap<String, Any?>) {
val name: String by mapA
val age: Int by mapA
var address: String by mapB
var id: Long by mapB
}
object SampleBy {
#JvmStatic fun main(args: Array<String>) {
val user = User(mapOf("name" to "John", "age" to 30),
mutableMapOf("address" to "city, street", "id" to 5000L))
println("name: ${user.name}; age: ${user.age}; " +
"address: ${user.address}; id: ${user.id}")
}
}
Result:
name: John; age: 30; address: city, street; id: 5000
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
}