276
CHAPTER 10: Development
While this is valid code, the repetition of
person.
is annoying. Besides, the first four lines are
about constructing a person, while the next line has nothing to do with a person. It would be
nice if this
could be visually expressed, and the repetition could also be avoided. This is a
construct in Kotlin, and it reads as follows:
val person = Person().apply {
lastName = "Smith"
firstName = "John"
birthDay = "2011-01-23"
}
company = Company("ACME")
This looks more expressive compared to the original code. It clearly says construct a person,
do something with it, and then do something else. There are five such constructs, and
despite
being similar, they differ in meaning and usage:
also
,
apply
,
let
,
run
, and
with
.
Table
10-3
describes them.
Table 10-3.
Scoping Functions
Syntax
What Is
this
What is it
Returns
Use
a.also {
... }
this
of
outer
context
a
a
Use for some
crosscutting concern,
for example to add
logging.
a.apply {
... }
a
-
a
Use for
postconstruction
object forming.
a.let {
... }
this
of
outer
context
a
Last expression
Use for
transformations.
a.run {
... }
a
-
Last expression
Do
some computation
using an object, with
only side effects. For c
better clarity, don’t use
what it returns.
with(a) {
... }
a
-
Last expression
Group operations on
an object. For better
clarity, don’t use what
it returns.
Using scoping functions greatly improves the expressiveness of your code. I use them often
in this book.
277
CHAPTER 10: Development
Nullability
Kotlin addresses the problem of nullability
on a language level, to avoid annoying
NullPointerException
throws. For any variable or constant, the assignment of
null
values
is not allowed by default; you have to explicitly declare nullability by adding a
?
at
the end as
follows:
var name:String? = null
The compiler then knows that
name
from the example can be
null
and takes various
precautions to avoid
NullPointerException
s. You, for example,
cannot write
name.
toUpperCase()
, but you have to use
name?.toUpperCase()
instead, which does the
capitalization only if
name
is not
null
and otherwise returns
null
itself.
Using the scoping functions we described earlier, there is
an elegant method to avoid
constructs like
if( x != null ) { ... }
. You can instead write the following:
x?.run {
...
}
This does the same but is more expressive; by virtue of the
?.
, the execution of
run{}
happens only if
x
is not
null
.
The
elvis
operator
?:
is also quite useful because it handles cases where you want to
calculate an expression only
if the receiver variable is
null
, as follows:
var x:String? = ...
...
var y:String = x ?: "default"
This is the same as
String y = (x != null) ? x : "default");
in Java.
Data Classes
Data classes are classes whose responsibility is to carry structured data. Actually doing
something with the data inside the data class usually is not necessary or at least not
important.
The declaring of data classes in Kotlin is easy; all you have to do is write the following:
data class Person(
val fName:String,
val lName:String,
val age:Int)
Or, if you want to use default
values for some arguments, use this:
data class Person(
val fName:String="",
val lName:String,
val age:Int=0)