MURAT YENER: Delegates help you delegate tasks to other objects
and provide better coder use, which you
can learn more in this video.
Kotlin not only supports an easier way
to implement delegates with by keyword,
but also provides built-in delegates
such as lazy, observable, vetoable,
and not null in the Kotlin standard library.
Let's see these built-in delegates and how
they work under the hood.
But, before that, I bet you'll like this video
and want to subscribe to the channel.
Don't you?
[MUSIC PLAYING]
The lazy function is a property delegate
that helps you to lazily initialize properties.
That's when they are first accessed.
Lazy can be really helpful for objects
which are expensive to create.
Lazy takes two parameters, a LazyThreadSafetyMode enum value
and a lambda.
The LazyThreadSafetyMode parameter
specifies how the initialization is synchronized
between different threads.
Lazy uses a default value of LazyThreadSafety
Mode.SYNCHRONIZED, which means the initialization is thread
safe at the cost of a slight performance impact
from explicit synchronization.
The lambda is executed when the property
is accessed for the first time.
And its value is stored for future access.
When checking the decompiled Java code,
we see that the Kotlin compiler creates
a reference of object type lazy for the lazy delegate.
Let's take a look at the source code of lazy function.
Since the lazy function uses LazyThreadSafetyMode
synchronized by default, it returns a lazy object
of type synchronized lazy input class.
When the delegated property is first accessed,
the getvalue function of SynchronizedLazyImpl
is called, which initializes the property
in a synchronized block.
This guarantees that the lazy object is initialized
in a thread safe manner, but it's the performance cost
of the synchronized block.
If you are sure that this resource will be initialized
by a single thread, you can pass LazyThreadSafetyMode.NONE
to lazy and the function will not use the synchronized block
during initialization.
However, keep in mind that LazyThreadSafetyMode.NONE
doesn't change the synchronous nature of lazy initialization.
Lazy initialization still takes the same amount of time
as non-lazy initialization of the object on the first access.
This means objects which take long to initialize
can still block the UI thread if accessed from it.
Lazy initialization can be helpful to initialize
expansive resources.
However, for simple objects, such as a string,
lazy adds overhead by generating other objects,
such as lazy and KProperty.
Observable is another built-in delegate
of the Kotlin standard library.
Observer is a design pattern in which an object maintains
a list of its dependents called observers and notifies them
automatically when its state changes.
This pattern can be really helpful when
more than one object needs to be informed when a value has
changed, rather than having each dependent object periodically
call and check if the resource is updated.
Observable takes two parameters--
the initial value and the listener
handler that will be called when the value is modified.
Observable creates an observable property object
that executes the lambda you pass to the delegate
each time the setter is called.
Looking at the decompiled person class,
we see that the Kotlin compiler generates a class that extends
the observable property.
This class also implements a function called afterChange
that has the lambda function, which is passed
to the observable delegate.
The after change function is envoked
by the setter of the parent observable property class.
This means each time a caller sets a new value for address,
the setter will automatically mark the afterChange function,
resulting in all the listeners to be
notified about the change.
You can also see a call to beforeChange
in the decompiled code.
beforeChange is not used by the observable delegate,
but is used by the delegate which is coming up next.
Vetoable.
Vetoable is another built-in delegate
in which the property delegates veto rights to its value.
Similar to the observable delegate,
vetoable takes two parameters--
the initial value and the listener
handler that will be called when any caller wants to modify
the value of the property.
If the lambda function returns true,
the property value will be modified.
Else, the value will stay the same.
In this case, if the caller tries
to update the address with anything
smaller than 15 characters, the current value
will be preserved.
Looking at the decompiled person class,
Kotlin generates a new class, which
extends to observable property.
The generated class includes the lambda
we passed in beforeChange function, which
will be called by the setter before the value is set.
The last built-in delegate I'll talk about
is delegates.notNull.
NotNull simply allows the property
to be initialized at a later time.
notNull is similar to lateinit.
In most cases, lateinit is preferred,
since notNull creates an extra object for each property.
However, you can use notNull with primitive types,
which lateinit doesn't support.
notNull uses a special type of read-write property
named notNull var.
Looking at the decompiled code, the full name property
is initialized with the notNull function, which
returns a notNull var object.
The notNull var class simply holds
a generic nullable internal reference
and throws illegal state exception
if any call calls the getter before the value is
initialized.
The Kotlin standard library provides a set
of built-in delegates so you don't need to write, maintain,
and reinvent them.
Built-in delegates initialize fields lazily,
allow primitive types to be initialized later,
observe and get notified when a value changes,
and even veto those changes.
Thanks for watching.
Go write beautiful Android apps with Kotlin.
And I'll see you next time.
[MUSIC PLAYING]