I am reading this page about Delegated properties in Kotlin doc site.
import kotlin.reflect.KProperty
class Example {
var p: String by Delegate() // 1
override fun toString() = "Example Class"
}
class Delegate() {
operator fun getValue(thisRef: Any?, prop: KProperty<*>): String { // 2
return "$thisRef, thank you for delegating '${prop.name}' to me!"
}
operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String) { // 2
println("$value has been assigned to ${prop.name} in $thisRef")
}
}
fun main() {
val e = Example()
println(e.p)
e.p = "NEW"
}
The output is:
Example Class, thank you for delegating 'p' to me!
NEW has been assigned to p in Example Class
And I can understand the result.
But my question is, what if I print the e.p again after setting its value as NEW:
fun main() {
val e = Example()
println(e.p)
e.p = "NEW"
println(e.p) // print it again after setting new value on it
}
I expect it to print NEW. But the actual result is, it kept the same as the 1st println: Example Class, thank you for delegating 'p' to me!.
Kotlin playground at here.
Seems like the e.p = "NEW" cannot change the value correctly. What causes that? What do I do if I want to set the value as NEW?
Your delegate class instance completely takes over what the getter and setter of the property do. Since your delegate’s setValue function doesn’t actually store the passed-in value in any internal property, it’s getValue function has no way to retrieve and return it. And indeed, the implementation of getValue() in your code is only generating a String and returning that.
When you get the value of the delegated property, it returns whatever your delegate returns in getValue(), so the behavior is determined by how you program your delegate class.
Related
I have a specific question about the usage of generics in Kotlin.
I want to create a function which takes a generic T as an argument.
It uses that to assign name from one of the classes: Class1 or Class2 to the local variable testString.
Unfortunately this is only possible when I check the type of the argument with the if conditions.
This leads to duplicate code. If I try to avoid that and use Line 12 I get this error during compile time: Unresolved reference: name
Is it possible in Kotlin to avoid the if conditions and use the testString assignment only once when the classes you are going to use have the same property with the same name?
Code:
fun main() {
val class1 = Class1("Foo1")
val class2 = Class2("Foo2")
}
class Class1(val name: String)
class Class2(val name: String)
fun <T> doStuff(classOneOrTwo: T) {
var testString: String
testString = classOneOrTwo.name //not working: Unresolved reference: name
if (classOneOrTwo is Class1) {
testString = classOneOrTwo.name
}
if (classOneOrTwo is Class2) {
testString = classOneOrTwo.name
}
}
You don't need generics here.
You can just write an interface that requires its implementers to have a name property.
interface HasName {
val name: String
}
Class1 and Class2 should implement the interface:
class Class1(override val name: String): HasName
class Class2(override val name: String): HasName
Then doStuff can be written as:
fun doStuff(classOneOrTwo: HasName) {
var testString = classOneOrTwo.name
// ...
}
You can make doStuff generic:
fun <T: HasName> doStuff(classOneOrTwo: T) {
var testString = classOneOrTwo.name
// ...
}
But you don't gain anything in particular by doing so.
Non-reified* generics are the most helpful when you want to establish some kind of "link", whether it be between parameters, or between parameters and the return type. For example, if your method is supposed to return the same type of thing as it takes:
fun <T> doStuff(foo: T): T { ... }
Or your method takes two parameters, and the second parameter must be the element type of the first parameter, which is a mutable list:
fun <T> doStuff(list: MutableList<T>, t: T) { ... }
* This paragraph doesn't quite apply to reified generics, which could be useful on their own.
Class1 and Class2 have nothing in common for the doStuff function to resolve the property name even though they were written exactly the same way, if you expect that just because you have a generic parameter T everything will be automatically be resolved, unfortunately the compiler doesn't know what T is here, aside from it being implcitly Any? type, (i.e <T: Any?>).
You're having a compile error here becase name is not a property of Any?
classOneOrTwo.name //not working: Unresolved reference: name
However, calling the doStuff function compiles fine because everything in Kotlin is a direct or indirect child of Any?
fun main() {
val class1 = Class1("Foo1")
val class2 = Class2("Foo2")
doStuff(class1)
doStuff(class2)
}
and if you try to invoke some function using classOneOrTwo param and pressed cltr+click on it, youll see its a function of the type Any?
fun <T> doStuff(classOneOrTwo: T) {
...
...
classOneOrTwo.toString() // <-- ctrl + click this you'll see its a function of Any?,
You should create a hierarchy (Inheritance) where Class1 and Class2 can inherit something from, in your case name
open class ParentClass(open val name: String)
class Class1(override val name: String) : ParentClass(name)
class Class2(override val name: String) : ParentClass(name)
fun <T: ParentClass> doStuff(classOneOrTwo: T) {
Log.e("DoStuff", classOneOrTwo.name) // now this works find because
}
Now it works because you explicitly tell the compiler that T is a type of ParentClass where the name property exists, and is inherited by your Class1 and Class2.
Back to your main function
doStuff(class1)
doStuff(class2)
prints,
Foo1
Foo2
This seems to me like a trivial problem, but I was unable to find an answer, so here it is !
I'm writing a simple Delegated property which I want to use like this ( with an initial value ) :
val name by MyDelegate ("Initial Name")
Here is my class for the delegated Property :
class MyDelegate (val init: String) {
var initialized = false
operator fun getValue(thisRef:Any, property: KProperty<*>): String
{
if (!initialized) {
setValue(thisRef,property,init)
initialized = true
}
// return the actual value
}
operator fun setValue(thisRef:Any, property: KProperty<*>, value: String) {
// set value
}
}
Is there a less ugly way to create a delegated property with an initialization, saving the use of one Boolean and one test every time ?
Thank very much you for your attention !
Can we implement Rust like Traits and generic Traits using Kotlin Interfaces?
Also is there any way of using fn(&self) like construct in Kotlin class/interface default function implementations?
Can some examples be shown please?
Thanks
I don't know much about Rust, I'm referrring to these two videos as for what you're talking about, generic traits and &self explaination.
In kotlin you'd implement them using interfaces and classes as you've guessed.
An example of that is:
interface GenericTrait { // Same as traits
// <T:Any> just makes method to be called for non-null values, if you use <T>, you can pass null as well
fun <T: Any> method(value: T)
}
class TraitImpl : GenericTrait { // Same as structs
val isDisabled = Random.nextBoolean() // instance variable
// you can access instance parameter using the this or even not using it at all as in below
override fun <T: Any> method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
// or explicitly call ${this.isDisabled}, both are the same
}
}
fun main() {
TraitImpl().method("Hello")
TraitImpl().method(23)
TraitImpl().apply { // this: TraitImpl
method(23)
method(Unit)
}
}
Result:
Type of value is class kotlin.String, and the value is Hello. I am true
Type of value is class kotlin.Int, and the value is 23. I am true
Type of value is class kotlin.Int, and the value is 23. I am false
Type of value is class kotlin.Unit, and the value is kotlin.Unit. I am false
You can extract implementation outside if you want as an extension function just like you do in Rust.
interface GenericTrait {
val isDisabled: Boolean
}
class TraitImpl : GenericTrait {
override val isDisabled = Random.nextBoolean()
}
// define methods out of class declaration
fun <T: Any> GenericTrait.method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
}
I am trying to understand how the delegate keyword by works.
So delegating to implemenent an interface is clear e.g.
class Manager(clientele: List<Client> = ArrayList()): List<Client> by clientale
But the following does not work:
data class Client(val name: String, val postalCode: Int)
fun createClient() = Client("Bob", 1234)
val bigClient: Client by createClient() // compilation error
I get the error:
Missing getValue(Nothing?, KProperty<*>) method delegate of type
Client
I thought that if two objects are the same the delegation from one to the other (Client by Client) would work.
Can someone please explain what is the error here and what am I doing wrong?
Unfortunately that's not exactly how delegation of properties works. Based on the documentation:
For a read-only property (i.e. a val), a delegate has to provide a function named getValue that takes the following parameters:
thisRef - must be the same or a supertype of the property owner;
property - must be of type KProperty<*> or its supertype.
For a mutable property (a var), a delegate has to additionally provide a function named setValue that takes the following parameters:
thisRef - same as for getValue();
property - same as for getValue();
newValue - must be of the same type as the property or its subtype.
[...] Both of the functions need to be marked with the operator keyword.
So in order just to make your example work, you have to add a getValue() method which meets the above requirements:
data class Client(val name: String, val postalCode: Int) {
operator fun getValue(thisRef: Nothing?, property: KProperty<*>): Client = this
}
You can also use and implement the ReadOnlyProperty and ReadWriteProperty interfaces which provide the required methods:
data class Client(val name: String, val postalCode: Int) : ReadOnlyProperty<Nothing?, Client> {
override fun getValue(thisRef: Nothing?, property: KProperty<*>): Client = this
}
Edit:
What is this getValue() supposed to do?
Let me explain a little further on a more abstract example. We have the following classes:
class MyDelegate : ReadWriteProperty<MyClass, String> {
private var delegateProperty: String = ""
override fun getValue(thisRef: MyClass, property: KProperty<*>): String {
println("$thisRef delegated getting the ${property.name}'s value to $this")
return delegateProperty
}
override fun setValue(thisRef: MyClass, property: KProperty<*>, value: String) {
println("$thisRef delegated setting the ${property.name}'s value to $this, new value: $value")
delegateProperty = value
}
}
class MyClass {
var property: String by MyDelegate()
}
The above MyClass would get compiled more or less to:
class MyClass {
private var property$delegate: MyDelegate = MyDelegate()
var property: String
get() = property$delegate.getValue(this, this::property)
set(value) = property$delegate.setValue(this, this::property, value)
}
So you can see that the compiler requires a delegate to have getValue() and setValue() methods for mutable properties (var) or only getValue() for immutable properites (val), because it uses them to respectively get and set the delegated property's value.
What are Nothing and KProperty<*>?
KProperty<*> is a Kotlin class which represents a property and provides its metadata.
Nothing is a type that represents a value that doesn't exist. It's quite irrelevant from the delegation point of view. It came up in this case, because you probably defined the bigClient property outside any class so it has no owner, hence thisRef is Nothing.
Is it possible to have a variable that can holds any type function.
Like :
fun method1(par: Boolean){}
fun method2(par: Boolean) : Int{return 1}
fun method3(par: Boolean, par2: Boolean) : Int{return 1}
var funtionHolder : ((Any)->Any) ?= null //What should I write here?? so to hold any type of function
fun method4(){
.........
funtionHolder = ::method1 //getting compile time error
.........
funtionHolder = ::method2 //getting compile time error
.........
funtionHolder = ::method3 //getting compile time error
}
After holding the function_reference I need to invoke it later. So I need to holds it parameter type and state also.
You can hold them in a KFunction<Any> or its superclass KCallable<Any> because you know nothing about the parameter list and nothing about the return type, so you have to go to something that can reference at that level of abstraction. These instances can then be invoked more generically using the call() or callBy() methods. (this requires the kotlin-reflect dependency). To do something safer and to call like a normal function you'd have to cast back to the specific function type later.
If you want to avoid this, you'll need to unify your signatures to something you can point to with another function type (i.e. KFunction1 or KFunction2). Otherwise how you'll call this, what you'll do with it will be up to you at this point because you erased all the information that allows you to easily call the function.
val functionHolder1: KFunction<Any> = ::method1 // success!
val functionHolder2: KFunction<Any> = ::method2 // success!
val functionHolder3: KFunction<Any> = ::method3 // success!
You can then make a DeferredFunction class to hold these along with parameters you want to later pass, and then invoke it whenever in the future.
class DeferredFunction(val function: KFunction<Any>, vararg val params: Any?) {
#Suppress("UNCHECKED_CAST")
operator fun <T> invoke(): T {
return function.call(params) as T
}
}
fun whatever(name: String, age: Int): String {
return "$name of age $age"
}
val functionHolder = DeferredFunction(::whatever, "Fred", 65)
println(functionHolder<String>()) // "Fred of age 65"
You do not need the generic return type on the invoke function and could just make it return Any or call it as functionHolder<Any>() but it is nice if you know what to expect for the return. You can decide what to do there based on your actual use case. Also no need to special case for no parameters, just don't pass any, i.e. DeferredFunction(::otherFunc)
With reference from Jayson's answer, added extra code to hold the state of the function by using vararg and spread operator(*).
var functionHolder: KFunction<Any> ?= null
var paramsHolder : Array<out Any?> ?= null
fun hold(functionReference : KFunction<Any>, vararg args : Any?) {
this.functionHolder = functionReference
this.paramsHolder = args
}
fun release() {
if (functionHolder != null) {
if (paramsHolder != null) {
functionHolder?.call(*paramsHolder!!)
} else {
functionHolder?.call()
}
}
}
......
fun method3(par: Boolean, par2: Boolean) : Int{return 1}
......
hold(::method3, true, false)
release()//it works
No. Kotlin is static typed language and doesn't allow this. Else what happens when this is called?
functionHolder->invoke(3)
and when functionHolder is assigned a lamda that doesn't take parameter?