Kotlin will be the official language for Android was announced at Goole I / O 2017. This post is intended for Java programmers and describes some useful Kotlin features not found in Java.
This post is written so that it can be understood independently, but it is the third in the series below. It is assumed that you understand the basic syntax of Kotlin, so if you do not know the syntax of Kotlin itself, please see the previous post first.
Learn about some useful Kotlin features that Java doesn't have.
It says "only Kotlin", but it's just a comparison with Java. This post is for Java programmers. Many say it's in other languages (though not in Java).
I've mentioned a lot, but my favorite is Data Class and the last one I mentioned ʻapply`. It's really convenient.
String Template
This is often called string interpolation in other languages.
// Kotlin
val a = 2
val b = 3
println("$a + $b = ${a + b}") // 2 + 3 = 5
You can embed the value of the variable foo
in a string in the form $ foo
. You can also write any expression in the ...
part by writing like $ {...}
. You don't have to combine strings with +
as you do in Java.
Kotlin allows multi-line string literals. This is useful for creating string literals with line breaks and "" like ↓. You can also use String Templates like $ title
in multi-line string literals.
// Kotlin
val title = "Hello, World!"
val headerId = "header"
val header = "Hello, World!"
val html = """<!DOCTYPE html>
<html>
<title>$title</title>
<body>
<h1 id="$headerId">$header</h1>
</body>
</html>
"""
println(html)
↓ is the output result.
<!DOCTYPE html>
<html>
<title>Hello, World!</title>
<body>
<h1 id="header">Hello, World!</h1>
</body>
</html>
Kotlin allows you to create class-independent ** _ Function _ **, unlike Java. Those who are new to Java may not be familiar with the term function, but it's like an independent method that doesn't belong to a class or instance. Its role is similar to Java's static
method.
For example, Kotlin's listOf
is a function. Since it doesn't belong to a class, it doesn't need a part of ʻArrays. unlike the
static method ʻArrays.asList
.
// Java
List<Integer> list = Arrays.asList(2, 3, 5);
// Kotlin
val list = listOf(2, 3, 5)
Frequently used functions such as listOf
and mapOf
are useful to be called without a class name like this (even in Java, ʻimport static can do it, but ʻimport
unless it is used repeatedly. It's a hassle to do).
Unlike the static
method, functions do not have namespaces separated by class name, so if you make anything into a function, name conflicts will occur. However, it is convenient not to abuse it by creating a private
function only in a file or a ʻinternal` function limited in a certain module.
Single-Expression function
A function that can be implemented with a single expression (not only a function but also a method) can be described concisely as ↓.
// Kotlin
//Normal writing
fun square(number: Int): Int {
return number * number
}
// Kotlin
//Concise writing
fun square(number: Int): Int = number * number
The code can be compressed and it is much cleaner.
You can also assign a function to a variable.
// Kotlin
val square: (Int) -> Int = { it * it }
square(3) // 9
Notice that the type of square
is (Int)-> Int
. This means a function that takes one ʻInt as an argument and returns ʻInt
. If you write (Foo, Bar)-> Baz
, it will be the type of function that receives Foo
as the first argument and Bar
as the second argument and returns Baz
.
Being put in a variable can also be passed as an argument.
val list = listOf(2, 3, 5)
list.map(square) // [4, 9, 25]
Kotlin allows you to give default arguments instead of overloading functions and methods.
// Java
static double log(double x, double base) {
...
}
static double log(double x) {
return log(Math.E);
}
// Kotlin
fun log(x: Double, base: Double = Math.E): Double {
...
}
You can also give multiple default arguments and specify only one of them.
// Kotlin
fun foo(x: Int = 42, y: Boolean = true, z: String = "xyz") {
...
}
foo(y = false) // x = 42, z = "xyz"
In fact, Kotlin operators are methods. For example, +
is a method called plus
, and writing 1 + 2
is equivalent to writing 1.plus (2)
. To implement a method that acts as an operator, add the ʻoperator` qualifier.
// Kotlin
class Vector2(val x: Double, val y: Double) {
operator fun plus(vector: Vector2): Vector2 {
return Vector2(x + vector.x, y + vector.y)
}
}
Vector2(2.0, 3.0) + Vector2(4.0, 5.0) // Vector2(6.0, 8.0)
The correspondence between operators and methods of four arithmetic operations is as follows.
operator | Method |
---|---|
a + b |
a.plus(b) |
a - b |
a.minus(b) |
a * b |
a.times(b) |
a / b |
a.div(b) |
See the official documentation (https://kotlinlang.org/docs/reference/operator-overloading.html) for more details.
The ones that I find particularly useful are get
and set
. Each corresponds to []
(in an array in Java).
// Kotlin
val a = list[i] // list.get(i)
list[i] = b // list.set(i, b)
Java's List
and Map
's get
/ set
(put
) were annoying, but in Kotlin thanks to operator overloading, collection elements like[i]
You can access (in Kotlin you can also register an entry with the set
method in Map
).
Int.plus
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/plus.htmlList.get
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/get.htmlMutableList.set
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-mutable-list/set.htmlMutableMap.set
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/set.htmlDestructuring Declaration
Sometimes I want to temporarily pair two values and treat them, but not enough to create a class for that. In Java, you can always return only one method return value, but you may have wanted to return two return values once. In such a case, I had no choice but to make a crying arrangement or create a dedicated class.
Kotlin always has one return value, but instead it provides a generic class called Pair <out A, out B>
.
// Kotlin
val x = Pair(42, "xyz")
The convenience of this Pair
is that it can be assigned as ↓.
// Kotlin
val (a, b) = x
println(a) // 42
println(b) // "xyz"
This is called ** Destructuring Declaration **. In this case, if you create a method that returns Pair
, you can make it behave as if there are two return values.
// Kotlin
fun foo(): Pair<Int, String> {
return Pair(42, "xyz")
}
val (a, b) = foo()
In fact, this feature is not a proprietary patent of Pair
. Any class can implement this functionality using classes with ʻoperator fun such as
component1,
component2`.
// Kotlin
class Foo(val a: Int, val b: String) {
operator fun component1(): Int {
return a
}
operator fun component2(): String {
return b
}
}
val (a, b) = Foo(42, "xyz")
Of course, it can be decomposed into two or more values and assigned. In addition to Pair
, Kotlin's standard library also provides a class that holds three values, Triple
.
// Kotlin
val triple = Triple(42, "xyz", true)
val (a, b, c) = triple
However, be aware that abuse of Pair
and Triple
will make the type meaningless and impair readability. In particular, if you start to handle values as Pair
or Triple
instead of just using it temporarily as a return value, consider whether it is better to replace it with your own class.
Destructuring Declarations can also be used in combination with the for
loop. For example, in Kotlin, when you retrieve an element of List
in a for
loop, you can retrieve the index along with it as follows:
// Kotlin
val list = listOf("a", "b", "c")
for ((index, element) in list.withIndex()) {
println("$index : $element")
}
0 : a
1 : b
2 : c
The return type of the withIndex
method is ʻIterable <IndexedValue . ʻIndexedValue <T>
implements component1
and component2
, so you can retrieve the value while decomposing it like ↑.
You can also iterate Map
to retrieve keys and values.
// Kotlin
val map = mapOf(
"a" to 2,
"b" to 3,
"c" to 5
)
for ((key, value) in map) {
println("$key : $value")
}
a : 2
b : 3
c : 5
This is because the Map <K, V>
ʻiterator method returns ʻIterator <Map.Entry <K, V >>
, and the component1
ofMap.Entry <K, V>
is the key. This is because component2
returns a value. The Map.Entry <K, V>
returned by the iterator next
is assigned to(key, value)
one by one and the loop is executed.
Furthermore, the above to
is actually generated by Pair
. " a "to 2
is the same as writing"a" .to (2)
. In Kotlin, methods declared with the ʻinfixmodifier can be called infix notation in this way. Since
" a "to 2 =
"a" .to (2) =
Pair ("a", 2) , the part of
mapOfis written as follows using the constructor of
Pair`. can also do.
// Kotlin
val map = mapOf(
Pair("a", 2),
Pair("b", 3),
Pair("c", 5)
)
Since ʻA and
Bof
Pair <A, B> do not have a separate relationship of ʻA
to B
, it is assumed that this to
is used together with mapOf
. I think it has been done. Most languages often have special literals and syntax for Map
, but Kotlin tends to try to solve with the language syntax as much as possible, and to
is one of the typical ones. It is one.
Pair
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/Triple
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/index.htmlwithIndex
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/with-index.htmlIndexedValue
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-indexed-value/index.htmlEntry
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/-entry/index.htmlto
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/to.htmlData class
With the primary constructor, it was easy to put together the declaration and implementation of the constructor, fields, getters, and setters. But ** _Data Class _ ** makes it even easier.
Often classes have only getters and setters, just hold values as data. Such a class is called a Data Class in Kotlin.
In such classes, in addition to constructors and properties, there are often routine implementations of ʻequals,
hashCode, and
toString. In Java, those codes are generated by the function of the IDE, but in Kotlin, you just add
dataas a modifier to the class declaration. Then, implementations of
hashCode, ʻequals
, toString
are created behind the scenes and can be treated as if they existed.
// Java
public final class Person {
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
@Override
public int hashCode() {
return ...;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Person)) {
return false;
}
Person person = (Person)object;
return firstName.equals(person.firstName) && lastName.equals(person.lastName) && age == person.age;
}
@Override
public String toString() {
return "Person(firstName=" + firstName + ", lastName=" + lastName + ", age=" + age + ")";
}
}
// Kotlin
data class Person(val firstName: String, val familyName: String, val age: Int)
In addition, Data Class automatically generates methods such as component1
, component2
for each property. Therefore, you can also use Destructuring Declaration on an instance of Data Class.
// Kotlin
val person = Person("Albert", "Einstein", 28)
val (firstName, familyName, age) = person
And the most useful thing about Data Class is the copy
method.
In modern programming, the idea is that what can be immutable should be as immutable as possible. Mutable instances tend to cause problems when shared, so you have to return a copy depending on your needs. However, if you return a copy of an instance and the value it holds is a mutable instance, you must also copy it in a chain reaction (shared without a deep copy rather than a shallow copy). We cannot guarantee that mutable instances will not cause problems). To avoid such complexity, it is simpler to separate the parts that can be immutable from the parts that must be mutable, and to complete the immutable world with immutable.
However, even in an immutable world, there are cases where you want to change only part of an instance. When you want to change an instance of an immutable class, express the change by recreating a new instance with the changes.
// Kotlin
val person = Person("Albert", "Einstein", 28)
val newPerson = Person(person.firstName, person.lastName, person.age + 1)
As shown in ↑, it is troublesome to pass all properties to the constructor just to change one property. It might be tolerable if you have about 3 properties, but what if you have 20 properties? It's easier to write using the Data Class's copy
method.
The following copy
method is automatically generated in the Data Class.
// Kotlin
fun copy(firstName: String = this.firstName, lastName: String = this.lastName,
age: Int = this.age) = User(firstName, lastName, age)
Since the default arguments are set, you only need to specify the property you want to change.
// Kotlin
val person = Person("Albert", "Einstein", 28)
val newPerson = person.copy(age = person.age + 1)
This is not much different from changing a mutable instance with setter. ** It's great to be able to achieve immutability and ease of change at the same time **.
Extension
When writing an object-oriented language program, you may want to add methods to an existing class.
// Kotlin
(3 + 2) * (3 + 2) //I want to square the calculation result`+`Is done twice and is useless
(3 + 2).square() // `Int`To`square`I wish I had a method
Of course, you can create a function instead of a method and pass the object as the first argument.
// Kotlin
fun square(value: Int): Int = value * value
square(3 + 2)
However, if you want to concatenate multiple processes with a function, it is inconvenient because the order of the processes and the order in the code are reversed.
// Kotlin
//Execute in the order of foo → bar → baz → qux
qux(baz(bar(foo(x))))
Methods can be chained in order.
// Kotlin
//Execute in the order of foo → bar → baz → qux
x.foo().bar().baz().qux()
Kotlin allows you to add methods (like) to your class later. This mechanism is called ** Extension **.
// Kotlin
fun Int.square(): Int = this * this
(3 + 2).square()
However, the methods added by Extension are different from regular methods.
Ordinary methods are dynamically resolved at run time which implementation is actually called.
// Kotlin
open class Animal {
open fun foo(): Int = 2
}
class Cat: Animal() {
override fun foo(): Int = 3
}
val animal: Animal = if (Math.random() < 0.5) Animal() else Cat()
animal.foo() // `animal`But`Animal`If`2` 、 `Cat`If`3`
However, the methods added by Extension are statically resolved at compile time. Polymorphism is not possible.
// Kotlin
open class Animal
class Cat: Animal()
fun Animal.foo(): Int = 2
fun Cat.foo(): Int = 3
val animal: Animal = if (Math.random() < 0.5) Animal() else Cat()
animal.foo() // `animal`But`Animal`But`Cat`But`2`
Extension is not rewriting the class itself. The substance of the method added by Extension is a function. A function that looks like such a method is called ** Extension Function **. The behavior of the code in ↑ is easy to understand if you think of it as an ordinary function like ↓.
// Kotlin
open class Animal
class Cat: Animal()
fun foo(animal: Animal): Int = 2
fun foo(cat: Cat): Int = 3
val animal: Animal = if (Math.random() < 0.5) Animal() else Cat()
foo(animal) // `animal`But`Animal`But`Cat`But`2`
Which of the overloaded foo
s is called is determined at compile time. Since ʻanimal is of type ʻAnimal
, which foo
is called, whether it is ʻAnimal or
Cat, is determined by the type of the value passed to the argument (in this case ʻAnimal
). Will be done.
Why does Extension do this confusing thing without rewriting the class itself? If you rewrite the class itself, if you happen to add a method with the same name between different libraries, the method implementation will conflict. If libraries A and B both add a method called foo
to the same class, it is possible that the foo
method in library A will be called unexpectedly in library B. That's scary and I can't use Extension. Such confusion cannot occur if it is resolved statically at compile time.
It is convenient to be able to call the Extension Function in the form of a method, but it can be confusing that the behavior changes depending on whether it is a real method or an Extension Function, even though it looks the same from the user side. Don't add an Extension Function with the same name in a superclass and a subclass, as it can be confusing.
object
In some cases, like a singleton, a class needs only one instance. Kotlin makes it easy to create singletons by using the keyword ʻobject instead of
class`.
// Java
class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {}
public static Foo getInstance() {
return INSTANCE;
}
public int bar() {
return 42;
}
}
Foo.getInstance().bar(); // 42
// Kotlin
object Foo {
fun bar(): Int = 42
}
Foo.bar() // 42
It may look like a static
method in Java because it'sFoo.bar ()
, but Foo
is a class and an instance. As proof of that, you can also assign Foo
to a variable of type Foo
.
// Kotlin
val foo: Foo = Foo
foo.bar() // 42
It's easy not to do something like Foo.getInstance ()
, and unlike a class that only has a static
method, ʻobject` is also an instance, so you can inherit from other classes. It's convenient because you can even do polymorphism.
// Kotlin
interface Foo {
fun bar(): Int
}
object Baz : Foo {
override fun bar(): Int = 42
}
Baz.bar() // 42
val foo: Foo = Baz
foo.bar() // 42
Note that Kotlin uses this ʻobject` to create an anonymous inner class. This is because there is always only one instance of an anonymous inner class.
// Java
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
...
}
});
// Kotlin
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(view: View) {
...
}
})
By the way, Kotlin can also use SAM conversion like Java 8, so in reality, the code above can be written concisely as follows.
// Kotlin
button.setOnClickListener { view ->
...
}
Sealed Class
You may want to create a class or interface and limit it to just this class that can inherit / implement it.
For example, suppose you create a type that maps JSON. The JSON specification is clearly defined at here, and the only accepted JSON values are object, array, string, number, true, false, and null. Let's express this by type.
Let's assume that they are represented by JSONObject
, JSONArray
, JSONString
, JSONNumber
, JSONTrue
, JSONFalse
, and JSONNull
, respectively. And consider the JSONValue
interface as their supertype.
// Kotlin
interface JSONValue
class JSONObject(val value: Map<String, JSONValue>) : JSONValue
class JSONArray(val value: List<JSONValue>) : JSONValue
class JSONString(val value: String) : JSONValue
class JSONNumber(val value: Double) : JSONValue
object JSONTrue : JSONValue
object JSONFalse : JSONValue
object JSONNull : JSONValue
Note that JSONTrue
, JSONFalse
, JSONNull
are ʻobject, not
class. Instances of these types correspond to JSON
true,
false, and
null, respectively, and are ʻobject
because there is only one.
However, this allows someone to add something like class JSONFoo: JSONValue
later. The JSON spec allows only the above seven JSON values, so I want to prevent other classes from implementing JSONValue
. Java cannot enforce such constraints, but Kotlin's ** Sealed Class ** does.
// Kotlin
sealed class JSONValue
class JSONObject(val value: Map<String, JSONValue>) : JSONValue()
class JSONArray(val value: List<JSONValue>) : JSONValue()
class JSONString(val value: String) : JSONValue()
class JSONNumber(val value: Double) : JSONValue()
object JSONTrue : JSONValue()
object JSONFalse : JSONValue()
object JSONNull : JSONValue()
Sealed Class can only be inherited from within the same file in which it was declared. Therefore, a third party cannot implement the subclass without permission.
Since the types of subclasses are limited, it works well with exhaustive branching by type using when
.
// Kotlin
val json: JSONValue = ...
when (json) {
is JSONObject -> { ... }
is JSONArray -> { ... }
is JSONString -> { ... }
is JSONNumber -> { ... }
is JSONTrue -> { ... }
is JSONFalse -> { ... }
is JSONNull -> { ... }
}
If you branch with ʻis, Smart Cast can treat
json as a checked type in the part of
{...} . For example, in the
{...}part of ʻis JSONObject-> {...}
, json
can be treated as a JSONObject
type.
Note that Sealed Class is a class, not an interface. However, you cannot instantiate the Sealed Class itself because it automatically becomes an abstract class. You can define only the ʻabstract` method in the Sealed Class body and use it as an interface, or you can write a common implementation.
Kotlin has a set of useful functions called scope functions. ʻAlso, ʻapply
, let
, run
, with
. However, although the name scope function is used in both Japanese and English, it could not be confirmed in the official document. → (Addition) [Original from the official blog](http://qiita.com/ngsw_taro/items/d29e3080d9fc8a38691e#%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%97% E9% 96% A2% E6% 95% B0% E3% 81% A8% E3% 81% 84% E3% 81% 86% E5% 90% 8D% E7% A7% B0% E3% 81% AB% E3% It looks like 81% A4% E3% 81% 84% E3% 81% A6).
@Ngsw_taro detailed explanation about the scope function, so I think it is especially useful here let
and ʻapply` I will only introduce about.
let
Let's say you want to do a calculation like x + 1 / x
. Now, if x
is found byfoo (42)
, the code to calculate x + 1 / x
can be written as:
// Kotlin
foo(42.0) + 1 / foo(42.0)
However, there is a problem with this code. That is, I've run foo (42)
twice. If foo
is a very heavy process, it is useless to do the same calculation twice. Or, if the process changes the result every time it is executed like Math.random ()
, the calculation result will change. To avoid that, you have to assign the result of foo (42)
to a variable once.
// Kotlin
val x = foo(42.0)
x + 1 / x
However, it is inconvenient because it becomes impossible to write in one expression if the assignment to the variable is inserted. Let
is useful in such a case. let
is an Extension Function implemented in the standard library for any type and can be used as follows:
// Kotlin
foo(42.0).let { x -> x + 1 / x }
The [implementation] of let
(https://github.com/JetBrains/kotlin/blob/1.1.2/libraries/stdlib/src/kotlin/util/Standard.kt#L62) looks like this: ..
// Kotlin
inline fun <T, R> T.let(block: (T) -> R): R = block(this)
That is, it passes itself to the function passed as an argument, and the return value of that function is the return value of let
itself.
As mentioned above, let
is convenient when you want to use the calculation result multiple times in one formula.
In addition, it is convenient to use it in combination with ?.
As shown in Last introduction.
// Kotlin
val x: Int? = ...
val square: Int? = x?.let { it * it }
The lambda expression {it * it}
above is only executed if x
is not null
. If x
is 3
, then square
becomes 9
. On the other hand, if x
is null
, it is x? .
, so let
itself is not executed and the result ( square
) is null
. This is the equivalent of map
or flatMap
in Java's ʻOptional`.
let
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/let.htmlapply
Last but not least, this is my favorite ʻapply`.
For a class with many parameters to set, you may want to create an instance and then read setter repeatedly to set it up. ʻApply` is useful in such cases.
For example, ↓ is the code excerpted from the JavaFX Oracle Tutorial (http://docs.oracle.com/javafx/2/get_started/hello_world.htm).
// Java
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
The code is verbose because I call setter
repeatedly, and the hierarchy is confusing and cluttered.
Rewriting this in Kotlin with ʻapply` gives:
// Kotlin
primaryStage.apply {
title = "Hello World!"
scene = Scene(StackPane().apply {
children.add(Button().apply {
text = "Say 'Hello World'"
onAction = EventHandler<ActionEvent> { println("Hello World!") }
})
}, 300.0, 250.0)
show()
}
In the lambda expression passed to ʻapply, the point is that you can access
titlewithout going through
primaryStage, like
title = "Hello World!" . This avoids repeatedly accessing members in the form of
primaryState.. This is because in the lambda expression passed to ʻapply
, the receiver of ʻapply,
primaryStage, is treated as
this. You can do this with Kotlin's [linkage mechanism](https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver) that associates the receiver with a function literal as
this`. Because.
Also, since the return value of ʻapply is the receiver itself (
primaryStagefor
primaryStage.apply {...}), eliminate the temporary variables such as
btn and
root` in the Java code. I can. This is easy to understand on a case-by-case basis, but if you want to create a temporary variable, you can also create it in Kotlin. I only assign to temporary variables when it's hard to tell what I'm doing without a name and it's likely to compromise the readability of the code.
ʻApply` is implemented as follows:
// Kotlin
inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
The T.
of this T. ()-> Unit
links the receiver to this
.
apply
: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.htmlThis post is intended to help Java programmers get started with Kotlin smoothly and is not an exhaustive explanation. If you want to learn Kotlin more comprehensively, I recommend you to use the Official Site Documents. Although it is in English, I think that Android application developers usually read English, and since it is plain English, I don't think there is a big problem.
Recommended Posts