How come Kotlin's built-in "lazy" function actually works? - kotlin

I wanted to try something new with delegated properties in my Kotlin code. What I found in docs is that for custom delegated properties I need to create a class with obligatory methods - getValue and optionally setValue, which are part of interfaces mentioned in the docs:
You can create delegates as anonymous objects without creating new classes using the interfaces ReadOnlyProperty and ReadWriteProperty from the Kotlin standard library
I started digging through Kotlin's built-in delegate functions. I looked into implementation of lazy function which looks like following:
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
And here's where my question stands: Why does it actually works? Lazy interface has only value property and some method determining its initialisation of the value. SynchronizedLazyImpl doesn't have much more than this. None of them have getValue or setValue methods, so why Kotlin doesn't complain and compiles successfully?

Looking at the source for Lazy here, you can see the following on line 37:
/**
* An extension to delegate a read-only property of type [T] to an instance of [Lazy].
*
* This extension allows to use instances of Lazy for property delegation:
* `val property: String by lazy { initializer }`
*/
#kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
Essentially, there's a getValue extension function for Lazy instances that just returns the value property. SynchronizedLazyImpl just defines the value property, and getValue is automatically provided.

Related

How does Kotlin's `lazy` implementation work without getValue and setValue?

In Kotlin properties with pre-set custom get/set behavior are implemented using delegated properties. According to the doc, a delegate for a property is just a class with getValue and setValue methods
But when I looked inside the implementation of lazy, I only found value property. So I tried to implement a delegate this way myself. It failed to compile, because getValue and setValue were not implemented explicitly. How does the official implementation of lazy work then?
getValue is declared as an extension function (yes that works too!) on all Lazy<T>s:
Source:
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
As you can see, it returns value, which is exactly what you would expect.
There is no setValue, and that is fine. That just means you can't use lazy { ... } as a property delegate for a var, and you indeed cannot:
var foo by lazy { 10 } // compiler error
After all, this doesn't make much sense anyway.

Confusing Property Delegation in Gradle's Kotlin DSL

Below is the code snippet that I came across in gradle's documentation
https://docs.gradle.org/current/userguide/tutorial_using_tasks.html
val hello by tasks.registering {
doLast {
println("Hello Earth")
}
}
hello {
doFirst {
println("Hello Venus")
}
}
In the above, hello is a TaskProvider type which provides task definition/action. The second call to hello is to extend the behavior of the task.
This delegate use looks slightly confusing to me. Following are the questions which are bugging me:
1) On inspecting the decompiled byte-code, I see tasks.registering returns RegisteringDomainObjectDelegateProviderWithAction object which should be used as the delegate and hence should provide getValue() and setValue() methods for delegate to work but as I saw, methods are not provided. Instead the class RegisteringDomainObjectDelegateProviderWithAction has a delegateProvider property of type tasks which is supposed to provide the delegate. Can any one help me understand, how delegation works here?
2) The second call is supposed to add behavior to the hello task. Since hello is a property, how are we able to pass a lambda/behavior to it? What am I missing?
I have already seen kotlin documentation which provides good explanation of delegates but doesn't aid in understanding the above case https://kotlinlang.org/docs/reference/delegated-properties.html
I would appreciate a detailed explanation as I am new to Kotlin.
Regarding the delegate use:
The delegation works via an extension operator method provideDelegate defined on RegisteringDomainObjectDelegateProviderWithAction:
operator fun RegisteringDomainObjectDelegateProviderWithAction<out TaskContainer, Task>.provideDelegate(
receiver: Any?,
property: KProperty<*>
) = ExistingDomainObjectDelegate.of(
delegateProvider.register(property.name, action)
)
The provideDelegate operator allows for more complex logic in delegate creation. As per the docs:
By defining the provideDelegate operator you can extend the logic of creating the object to which the property implementation is delegated. If the object used on the right hand side of by defines provideDelegate as a member or extension function, that function will be called to create the property delegate instance.
Regarding the "passing a lambda to a property":
This is implemented via overloading of the invoke operator as an extension function on the TaskProvider class:
operator fun <T> NamedDomainObjectProvider<T>.invoke(action: T.() -> Unit) =
configure(action)
Basically, the call hello { /* your lambda */ } is desugared into hello.invoke { /* your lambda */ }.

What is the benefit of having a private constructor and a use a method inside companion object to instantiate a class?

I've bumped into this code and I'm not sure why would anyone do this. Basically the author decided for making the class constructor private so that it cannot be instantiated outside the file, and added a public method to a companion object in the class that creates a new instance of this class. What is the benefit of this approach?
This is what I found:
class Foo private constructor(private val arg1: Any) {
//more code here..
companion object {
fun newFoo(arg1: Any) = Foo(arg1 = arg1)
}
}
Why is it better than this?
class Foo(private val arg1: Any) {
//more code here..
}
There are several benefits to providing a factory method instead of a public constructor, including:
It can do lots of processing before calling the construstor. (This can be important if the superclass constructor takes parameters that need to be calculated.)
It can return cached values instead of new instances where appropriate.
It can return a subclass. (This allows you to make the top class an interface, as noted in another answer.) The exact class can differ between calls, and can even be an anonymous type.
It can have a name (as noted in another answer). This is especially important if you need multiple methods taking the same parameters. (E.g. a Point object which could be constructed from rectangular or polar co-ordinates.) However, a factory method doesn't need a specific name; if you implement the invoke() method in the companion object, you can call it in exactly the same way as a constructor.
It makes it easier to change the implementation of the class without affecting its public interface.
It also has an important drawback:
It can't be used by subclass constructors.
Factory methods seem to be less used in Kotlin than Java, perhaps due to Kotlin's simpler syntax for primary constructors and properties. But they're still worth considering — especially as Kotlin companion objects can inherit.
For much deeper info, see this article, which looks at the recommendation in Effective Java and how it applies to Kotlin.
If you want to change Foo into an interface in the future the code based on the method will keep working, since you can return a concrete class which still implements Foo, unlike the constructor which no longer exists.
An example specific to android is, that Fragments should be constructed with an empty constructed, and any data you'd like to pass through to them should be put in a bundle.
We can create a static/companion function, which takes in the arguments we need for that fragment, and this method would construct the fragment using the empty constructor and pass in the data using a bundle.
There are many useful cases, for example what Kiskae described. Another good one would be to be able to "give your constructors names":
class Foo<S: Any, T: Any> private constructor(private val a: S, private val b: T) {
//more code here...
companion object {
fun <S: Any> createForPurposeX(a: S) = Foo(a = a, b = "Default value")
fun createForPurposeY() = Foo(a = 1, b = 2)
}
}
Call site:
Foo.createForPurposeX("Hey")
Foo.createForPurposeY()
Note: You should use generic types instead of Any.

why there is 'by' for the extended class and reified in function define

coming across a sample with a class and a function and trying to understand the koltin syntax there,
what does this IMeta by dataItem do? looked at https://kotlinlang.org/docs/reference/classes.html#classes and dont see how to use by in the derived class
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
class DerivedStreamItem(private val dataItem: IMeta, private val dataType: String?) :
IMeta by dataItem {
override fun getType(): String = dataType ?: dataItem.getType()
fun getData(): DerivedData? = getDataItem()
private inline fun <reified T> getDataItem(): T? = if (dataItem is T) dataItem else null
}
for the reference, copied the related defines here:
interface IMeta {
fun getType() : String
fun getUUIDId() : String
fun getDataId(): String?
}
class DerivedData : IMeta {
override fun getType(): String {
return "" // stub
}
override fun getUUIDId(): String {
return "" // stub
}
override fun getDataId(): String? {
return "" // stub
}
}
why the reified is required in the inline fun <reified T> getDataItem()? If someone could give a sample to explain the reified?
There is some good documentation on reified type parameters, but I'll try to boil it down a bit.
The reified keyword in Kotlin is used to get around the fact that the JVM uses type erasure for generic. That means at runtime whenever you refer to a generic type, the JVM has no idea what the actual type is. It is a compile-time thing only. So that T in your example... the JVM has no idea what it means (without reification, which I'll explain).
You'll notice in your example that you are also using the inline keyword. That tells Kotlin that rather than call a function when you reference it, to just insert the body of the function inline. This can be more efficient in certain situations. So, if Kotlin is already going to be copying the body of our function at compile time, why not just copy the class that T represents as well? This is where reified is used. This tells Kotlin to refer to the actual concrete type of T, and only works with inline functions.
If you were to remove the reified keyword from your example, you would get an error: "Cannot check for instance of erased type: T". By reifying this, Kotlin knows what actual type T is, letting us do this comparison (and the resulting smart cast) safely.
(Since you are asking two questions, I'm going to answer them separately)
The by keyword in Kolin is used for delegation. There are two kinds of delegation:
1) Implementation by Delegation (sometimes called Class Delegation)
This allows you to implement an interface and delegate calls to that interface to a concrete object. This is helpful if you want to extend an interface but not implement every single part of it. For example, we can extend List by delegating to it, and allowing our caller to give us an implementation of List
class ExtendedList(someList: List) : List by someList {
// Override anything from List that you need
// All other calls that would resolve to the List interface are
// delegated to someList
}
2) Property Delegation
This allows you to do similar work, but with properties. My favorite example is lazy, which lets you lazily define a property. Nothing is created until you reference the property, and the result is cached for quicker access in the future.
From the Kotlin documentation:
val lazyValue: String by lazy {
println("computed!")
"Hello"
}

Kotlin default arguments in interface bug?

kotlin file
interface Test {
fun test(message: String, delay: Int =100)
}
class A: Test
{
override fun test(message: String, delay: Int) {
}
}
I find i can't use #JvmOverloads in interface nor class.
if i add a #JvmOverloads in interface,the error is #JvmOverloads annotation cannot be used on interface method,if i add #JvmOverloads in class,the error is platform declaration clash....
However, I seem able to use defaults paramters in kotlin files,like this.
var a=A()
a.test("1234")
But when I use it in a java file, it seems that the method is not overloaded。
A a=new A();
a.test("123");//Compile error
The following version without interface can work
class A
{
#JvmOverloads
fun test(message: String, delay: Int=100) {
}
}
Then I can use it normally in java file
A a=new A();
a.test("123");
But how to maintain the same functionality after add the interface?
This is not a bug. #JvmOverloads annotation simply does not work with abstract methods.
From Kotlin docs:
Normally, if you write a Kotlin function with default parameter values, it will be visible in Java only as a full signature, with all parameters present. If you wish to expose multiple overloads to Java callers, you can use the #JvmOverloads annotation.
The annotation also works for constructors, static methods etc. It can't be used on abstract methods, including methods defined in interfaces.
source: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#overloads-generation
Why?
Because as You can learn from the doc I mentioned, #JvmOverloads instructs compiler to generate bunch of Java overloaded methods, omitting each of the parameters one by one, starting from the last one.
As far as I understand, each overloaded method calls internally method with one more parameter, and this additional parameter has default value. Edit: see comment by #hotkey here
This won't work with abstract methods, because they don't have any body.
Also new Java interface would have more methods, and its implementations would have to implement all of them. Kotlin interface had only one method.
To get to the same result you can make a LegacySupport class in Kotlin that will actually call the function with the default parameter and then you can expose only the return of the function to the java class from this class.