最近、Kotlin を勉強し始めました。
勉強していく過程で、よりKotlin を勉強したいと考えていく中で、以下の Kotlin の Introduction をまとめてみました。
https://play.kotlinlang.org/byExample/01_introduction/01_Hello%20world
まとめていく中で、以下の50項目に分けて記載してみました。
読みやすくするために1つの章をできるだけ簡潔にしています。
1. Hello World
Kotlin では、以下のように書く。
package org.kotlinlang.play
fun main() {
println("Hello World")
}
Kotlin のクラスは、基本的にはパッケージに属している。
もし、パッケージに属していない場合は、デフォルトパッケージとなる。
Kotlin のエントリーポイントは、main メソッドになる。Kotlin の1.3以降は、main メソッドの引数はいらない。
1.3よりも前の場合は、args: Array を引数に取る。
2. 基本的なメソッド
// 1
fun printMessage(message: String): Unit {
println(message)
}
// 2
fun printWithPrefix(message: String, prefix: String = "Info") {
println("[$prefix] $message]
}
// 3
fun sum(x: Int, y: Int): Int {
return x + y
}
1がKotlin の基本的なメソッドの形である。Stringを引数に取り、Unitを戻り値としている。
2は、デフォルト値を引数に渡した形である。
また、戻り値が省略されている。戻り値が省略されている場合は、Unit が戻り値となる。
3は、Int が戻り値となっているメソッドの形である。
3. 中置関数(Infix function)
中置関数を定義する際は、infix キーワードを用いる。
中置関数を呼び出す時、ドットと括弧を省略する事ができる。
中置関数は、以下の要件を満たしていないと定義する事はできない。
- メンバーメソッドもしくは拡張メソッドである必要がある。
- 引数は、1つでなければならない。また、可変の引数、デフォルト値を持つ引数であってはならない。
// 1
infix fun Int.times(str: String) = str.repeat(this)
// 2
2 times "Hello"
// 3
val pair = "program" to "Kotlin"
1は、Int の times を拡張している。拡張する際は、infix を用いる。
2は、infix を用いて拡張された関数の使用の仕方である。
本来であれば、2.times {"Hello"} のように書く必要があるが、infix で拡張すると、ドットや鉤括弧が不要となる。
3は、標準ライブラリでinfix で定義されているto を使用した例である。
ここでは、Pair オブジェクトを返している。
4. 演算子の拡張メソッド
演算子と対応しているメソッドを拡張する事で、演算子を用いた際の結果を変える事ができる。
operator fun Int.times(str: String) = str.repeat(this) + "abc"
println(2 * "Bye") // Bye Bye abc
operator を用いる事で、演算子を拡張する事ができる。
*と紐づいているメソッドがInt.times である。
そのため、Int.times メソッドを拡張する事で* 演算子を拡張する事ができる。
何の演算子と何のメソッドが紐づいているかについては、以下の公式ドキュメントを参照。
https://kotlinlang.org/docs/operator-overloading.html#unary-prefix-operators
5. 可変長引数とメソッド
キーワードとしてvararg を指定すると、可変長引数となる。
fun printEntry(vararg messages: String) {
for (m in messages) println(m)
}
printEntry("helle", "bye", "こんにちは")
// hello
// bye
// こんにちは
上記のように定義すると、String 型の可変長引数となる。そのため、複数の文字列を受け取る事ができる。
6. 変数
kotlin には型推論がある。明示的に型を指定することもできるが、コンパイラーによって自動的に型を推論する事が多い。
また、kotlin では、不変性を強制しているわけではないが、不変である方が望ましいとされている。
var a: String = "initial" // 1
val b = "String" // 2
1は、String型を定義している。2は、型推論に自動的にString型であると認識される。
また、1は、var を使用し、不変ではない状態となっている。2は、val を使用し、不変となっている。
7. Nullの安全性
普通は、kotlin では、Null を変数に代入することはできない。しかし、強制的にNull を代入する事もできる。Null を代入する時は、? を使用する。
var enableNull : String? = null // 1
fun useNull(enable: String?) { ~ } // 2
上記は、Null を受け入れるようにした変数とメソッドである。上記のように宣言する事で、Null を受け入れる事ができる。
8. クラス
クラスは、クラス名・ヘッダー・ボディーで構成されている。
ボディーは、中括弧で囲まれている。もし、ボディーがない場合は、中括弧を省略する事ができる。
class Customer // 1
class Cur(val type: String, val size: Int) //2
fun main() {
val customer = Customer()
val cur = Cur("Nissan", 4) // 3
}
1のように、パラメータがなくてもクラスを定義する事ができる。また、コンストラクタを作成しなくても、インスタンスを作成する事ができる。
パラメータが定義されているクラスの場合は、3のようにパラメータを渡す事でインスタンスを作成する事ができる。
9. Generic
Generic を使用する事で、特定の型に依存しない共通ロジックをカプセル化する事ができる。
class MutableStack<E> (vararg items: E) {
private val firstItem : E = items.first()
}
上記のクラスでは、E をGeneric な型として定義している。このE は、クラス内でも使用する事ができる。
そして、メソッドでもGeneric を使用する事ができる。
fun <E> mutableStackOf(vararg elements: E) = MutableStack(*elements)
fun main() {
val stack = mutableStack(0.2, 6, "a")
}
特定の型に縛られない状態であれば、上記のようにGeneric なメソッドを定義する事ができる。
10. 継承
Kotlin では、他の言語と同様に継承もサポートしている。
open class Dog {
open fun bark() {
println("ワン")
}
}
class Yorkshire : Dog() {
override fun bark() {
println("ワンワン")
}
}
Kotlin では、デフォルトでfinal としてクラスが宣言される。そのため、もし、継承したいなら、open キーワードを使用する。
そして、メソッドに関してもデフォルトでfinal で定義される。したがって、メソッドにおいても継承したいなら、open で定義する。
継承を行う際、スパークラスのデフォルトコンストラクターを呼び出す必要がある。そのため、Dog() という形になっている。
関数やプロパティを継承する際は、override キーワードを使用する。
open class Lion(val name: String, val origin: String) {
fun sayHello() {
println("$name, the lion from $origin")
}
}
class Asiatic(name: String) : Lion(name = name, origin = "India")
fun main() {
val lion: Lion = Asiatic("Rufo")
lion.sayHello() // Rufo, the lion from India
}
Asiatic クラスのname でval やvar 等のキーワードを使用していない。
これは、Lion クラスのname を呼び出しているからである。
11. When
switch の代わりとして when がある。
statement として使用する事もできるし、式として使用する事もできる。
fun cases(obj: Any) {
when (obj) {
1 -> println("One")
"hello" -> println("Hello")
is Long -> println("Long")
else -> println("unknown")
}
}
上記は、when statementの例である。
全ての条件分岐は、いずれかが満たされるまで順番にチェックされる。
fun whenExpression(obj: Any) : Any {
val result = when (obj) {
1 -> "one"
"hello" -> "Hello"
is Long -> "long"
else -> false
}
return result
}
上記は、when 式の例である。上記のように式としても利用できる。
12. ループ
- for
他の言語と同様にfor もサポートしている。
val jobs = listOf("lawer", "engineer", "designer")
for(job : jobs) {
println("JOB : $job")
}
- while, do-while
他の言語と同様にwhile , do-while を使用する事ができる。使用の仕方は他の言語と同じである。
fun main() {
var count = 0
while (count < 5) {
println("while $count")
count++
}
do {
println("do-while $count")
count++
} while (count < 5)
}
- iterator
自分自身のiterator も定義する事ができる。
class Animal (val name: String)
class Zoo (val animals: List<Animal>) {
operator fun iterator(): Iterator<Animal> {
return animals.iterator()
}
}
fun main() {
val zoo = Zoo(listOf(Animal("lion"), Animal("tiger")))
for (animal : zoo) {
println(animal)
}
}
iterator を定義する際は、iterator という名前にする必要がある。そして、operator キーワードで修飾する必要がある。
上記のiterator() では、next() メソッドとhasNext() メソッドを持っている。
13. 範囲
Kotlin では、範囲の取扱うにあたって様々な方法がある。
for (i in 0..3) println(i) //0123
for (i in 0 until 3) println(i) // 012
for (i in 2..8 step 2) println(i) // 2468
for (i in 3 downTo 0) println(i) // 3210
上記のように、.. , until , step , downTo のように、範囲の表現には、様々な方法がある。
また、以下のように、if の条件として使用する事も可能である。
var x = 0
if (x in 1..5) {
print("x is in 1 to 5")
count++
}
14. 等価チェック
== と === がある。
== は、構造のチェックに使用し、=== は、3章のチェックに使用する。
val authors = setOf("Shakespeare", "Hemingway", "Twain")
val writers = setOf("Twain", "Shakespeare", "Hemingway")
println(authors == writers) // 1
println(authors === writers) // 2
1は、構造をチェックするため、Trueが返される。
authors とwriters の参照は異なる。そのため、2は、Falseが返される。
15. 条件式
Kotlin には、三項演算子がない。その代わりに、条件式を使用する。
fun max (a: Int, b: Int) = if (a > b) a else b
print(max(10, 1)) // 10
16. データクラス
Kotlin では、値を持つだけのクラスをデータクラスとして簡単に定義できる。
データクラスは、以下のメソッドを自動で作成する。そして、getter やsetter も作成する必要はない。
equals()
hashCode()
toString()
componentN()
copy()
また、上記のメソッドをオーバーライドする事も可能である。
data class User(val name: String, val id: Int)
fun main() {
val user = User("Alex", 1)
val second = User("Bob", 2)
println(user.equals(second)) // false
println(user.hashCode()) // 63347075
println(user.toString()) // User(name=Alex, id=1)
println(user.copy()) // User(name=Alex, id=1)
println(user.component1()) // Alex
}
17. Enum クラス
色とか状態とか独立した値を固定値として表現する際、Enum クラスを使用する。
enum class State {
IDLE, RUNNING, FINISHED
}
fun main() {
val state = State.RUNNING
val message = when (state) {
State.IDLE -> "its idle"
State.RUNNING -> "its running"
State.FINISHED -> "its finished"
}
}
18. Sealed クラス
Sealed クラスは、継承を制限するためのクラスである。
Sealed クラスが宣言されているパッケージ内のサブクラスからのみ、継承する事ができる。
sealed class Mammal(val name: String)
class Cat(val catName: String) : Mammal(catName)
class Human(val humanName: String, val job: String) : Mammal(humanName)
19. オブジェクト
- オブジェクト式
Kotlin では、オブジェクトを式として扱う事ができる。
メソッド内で、1つのプロパティとしてオブジェクトを宣言し、そのメソッド内でオブジェクトのプロパティにアクセスする事ができる。
fun rentPrice(standardDays: Int, festivityDays: Int, specialDays: Int): Unit {
val dayRates = object {
var standard : Int = 30 * standardDays
var festivity: Int = 50 * festivityDays
var special: Int = 100 * specialDays
}
val total = dayRates.standard + dayRates.festivity + dayRates.special
print(total)
}
- オブジェクト宣言
オブジェクトとして宣言する事もできる。オブジェクト内でメソッドを定義する事もできる。
object DoAuth {
fun takeParams(userName: String, password: String) {
print("name: $userName, pass: $password")
}
}
- Companion オブジェクト
Java のstatic メソッドに似ている。クラス名を用いてオブジェクトメンバーを呼ぶ事ができる。
Companion オブジェクトの使用を考えているなら、パッケージレベルの関数の使用も同時に考えた上で、Companion オブジェクトの方がいいと思った場合に使用した方がいい。
class BigBen {
companion object Bonger {
fun getBongs(nTimes : Int) {
for (i in 1..nTimes) {
print("Bong")
}
}
}
}
fun main() {
BigBen.getBongs(12)
}
20. 高階関数
高階関数を簡単にいうと、パラメータとして関数を受け取り、関数を返す関数である。
高階関数は、第一級オブジェクトとなる。
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
return operation(x, y)
}
fun sum(x: Int, y: Int) = x + y
fun main() {
val sumResult = calculate(4, 5, ::sum) // 9
val mulResult = calculate(4, 5) {a, b -> a * b } // 20
}
上記の高階関数は、operation 関数を引数に受け取り、operation の処理結果を返している。
sumResult では、sum 関数を引数として渡している。:: を用いて参照する形で引数としている。
mulResult は、ラムダを引数として渡しているパターンである。このようにラムダを引数にする事も可能である。
21. ラムダ関数
以下は、引数で与えられた文字列を大文字に返すラムダの関数である。
以下からもわかる様に、ラムダには様々な書き方がある。
val upperCase1: (String) -> String = { str: String -> str.upperCase() } // 1
val upperCase2: (String) -> String = { str -> str.upperCase() } // 2
val upperCase3 = { str: String -> str.upperCase() } //3
val upperCase4: (String) -> String = { it.upperCase() } // 4
val upperCase5: (String) -> String = String::upperCase() // 5
1は、全ての型を記載したラムダである。
2は、ラムダの中の型を省略した形式である。これは、外の変数の型から中の型を推測する形式となっている。
3は、ラムダの外の型を省略した形式である。これは、中の変数の型から型を推測している。
(String) -> String と:String の外と中の型を同時に省略する事はできない。
4は、it を使用したラムダである。引数が1つの場合は、it を使用する事ができる。
5は、:: で関数を呼び出しているラムダである。ラムダが1つの関数を呼び出しで構成されている場合は、:: で関数を呼び出す事が可能である。
22. List
Kotlin のリストは、変更可能なミュータブルなリスト(MutableList)と読み取りだけ(List)の2つがある。
読み取り専用のリストを作成する際は、listOf() メソッドを使用する。ミュータブルなリストを作成する際は、mutableListOf() を使用する。
不要な変更を防ぐために、ミュータブルなリストをList でキャストする。
val user: MutableList<Int> = mutableListOf(1, 2, 3) // 1
val nonMutableUser: List<Int> = user // 2
1は、ミュータブルなリストを作成している。そして、2は、読み取り専用になっている。
そのため、1に対しては、値を追加する事ができるが、2に対しては、値を追加する事ができない。
23. Set
複製をサポートしていない順序がバラバラなコレクション。Set オブジェクト作成する際は、setOf() メソッドとmutableSetOf() メソッドを使用する。
リストと同様に、読み取り専用のSet を作成する際は、キャストする事で作成する。
24. Map
key と value のペアのコレクション。key は一意であり、関連する value を取得するために使用する。
Map を作成する時は、mapOf() とmutableMapOf() を使用する。
to という infix メソッドを使用する事ができ、このメソッドを使用する事で、initialize 時、煩雑に見えにくくなる。
読み取り専用のMap を作成する時は、リストと同様に、キャストする事で作成できる。
val mutableMap: MutableMap<Int, Int> = mutableMapOf(1 to 10, 2 to 20, 3 to 30)
上記のように、to を用いて Initialize する。
25. filter
ラムダのパラメータを受け取る。
このラムダのパラメータを各要素に適用し、true の要素を返す。
val list = listOf(2, 1, 0, -1, -2)
number.filter { x -> x > 0 }
number.filter { it > 0 }
上記のように、it を使用し、簡潔に書く事もできる。
26. map
コレクションの中で、全ての要素を変形する。map は、ラムダパラメーターとして、変形させる関数を受け取る。
val num = listOf(1, 2, 3, 4)
num.map { x -> x * 2 }
num.map { it * 3 }
27. any
boolean を返す。
要素の中に条件に最低1つでも合致する要素がある場合、true を返す。
val num = listOf(1, 2, 3, -1)
num.any { it > 0 }
28. all
boolean を返す。
全ての要素が条件に当てはまる場合、true を返す。
val num = listOf(1, 2, 3)
num.all { it > 0 }
29. none
boolean を返す。
条件に当てはまる要素がない場合、true を返す。
val num = listOf(1, 2, 3)
num.none { it < 0 }
30. find, findLast
find は、条件に合致した最初の要素を返す。findLast は、条件に合致した最後の要素を返す。
そして、全要素が条件に合致しなかった場合は、null を返す。
val words = listOf("something", "in", "and", "somehow")
val first = words.find { it.startsWith("some") } // something
val last = words.findLast { it.startsWith("some") } // somehow
val no = words.find { it.startsWith("nothing") } // null
31. first, last
first は、最初の要素を返す。last は、最後の要素を返す。
また、これらのメソッドには、条件を付与する事ができ、条件に合致した最初の要素や最後の要素を返すという使い方もできる。
コレクションが空だったり、条件に合致する要素がない場合は、NoSuchElementException を返す。
val num = listOf(1, 2, 3, 0, -1)
val first = num.first() // 1
val last = num.last() // -1
val numEven = num.first { it % 2 == 0 } // 2
val lastOdd = num.last { it % 2 != 0 } // -1
32. firstOrNull, lastOrNull
ほとんどfirst とlast と同様に作用する。
条件にマッチする要素がない場合、null を返すという点だけが異なる。空のコレクションに対して使用しても例外が発生せず、null となる。
val list = listOf("foo", "bar", "baz")
val emptyList = emptyList<String>()
emptyList.firstOrNull() // null
list.firstOrNull { it.startsWith('f') } // foo
list.lastOrNull { it.endsWith('z') } // baz
33. count
全要素の数か条件に合致した要素の数を返す。
val num = listOf(1, 2, 3)
num.count() // 3
num.cout { it % 2 == 0 } // 1
34. associateBy, groupBy
特定のキーによってインデックス付けされたコレクションの要素から map を作成する。
key は、keySelector パラメータに定義されている。また、value が定義されているvalueSelector を使用する事もできる。
associateBy とgroupBy の間で同じkey を使用したオブジェクトの処理の仕方が異なっている。
data class Person(val name: String, val city: String, val phone: String)
val people = listOf(
Person("john", "tokyo", "09012345678"),
Person("sarah", "kanagawa", "08067891234"),
Person("bean", "tokyo", "07011112222"))
val phoneAssociate = people.associateBy { it.phone }
// 09012345678=Person(name=john, city=tokyo, phone=09012345678), 08067891234=Person(name=sarah, city=kanagawa, phone=08067891234), 07011112222=Person(name=bean, city=tokyo, phone=07011112222)
val cityGroup = people.groupBy(Person::city, Person::name)
// tokyo=[john, bean], kanagawa=[sarah]
val cityAssociate = people.associateBy(Person::city, Person::name)
// tokyo=bean, kanagawa=sarah
associateBy は、条件に合った最後のvalue を取得する。groupBy は、条件に合う要素をリストにして取得する。
phoneAssociate は、電話番号とPersonの情報のMapを作成する。it.phone は、keySelector である。valueSelector は与えられていないため、value は、Personオブジェクトとなる。
cityGroup は、city とそこに住んでいるname のMapを作成する。tokyo に住んでいる人が2人いるため、tokyo=[john, bea] となっている。
cityAssociate は、city とそこに住んでいるname のMapを作成する。groupBy は、条件に合致する最後の要素を取得するため、tokyo=bean となる。
35. partition
コレクションを条件に対してtrue の要素と false の要素に分けるメソッド。
val list = listOf(1, 2, -1, -2)
list.partition { it % 2 == 0 } // [2, -2] [1, -1]
val (positive, negative) = list.partition { it > 0 } // positive: [1, 2], negative: [-1, -2]
36. flatMap
複数のコレクションの要素を反復可能で1つのリストに変換するメソッド。
val fruits = listOf("banana", "apple")
val cities = listOf("tokyo", "osaka")
val list = listOf(fruits, cities)
val listMap = list.map { it } // [["banana", "apple"], ["tokyo", "osaka"]]
val listFlat = list.flatMap { it } // ["banana", "apple", "tokyo", "osaka"]
37. minOrNull, maxOrNull
minOrNull は、コレクションの最小の要素を取得する。maxOrNull は、コレクションの最大の要素を取得する。
そして、コレクションが空の場合、null を返す。
val num = listOf(1, 2, 3)
val empty = emptyList<Int>()
val only = listOf(3)
num.minOrNull() // 1
empty.maxOrNull() // null
only.minOrNull() // 3
only.maxOrNull() //3
要素が1つしかない場合は、どちらのメソッドを使用してもその1つの要素を返す。
38. sorted
昇順で並べ替えたコレクションを返す。
sortedBy は、指定された関数で返された値を昇順にして返す。
val num = listOf(1, 2, -1, -2)
num.sorted() // -2, -1, 1, 2
num.sortedBy { -it } // 2, 1, -1, -2
39. Map へのアクセス
Map を使用する時、[] 演算子に与えられたkey に対応する値を返す。
そのMap にkey がない時は、null が返される。
[] と同様に、getValue メソッドでも同様の結果を得る事ができる。
しかし、getValue を使用した時に、Map にそのkey がない時は、例外がthrow される。
また、withDefault メソッドでMap が作成された場合は、getValue メソッドを使用し、key が存在しない場合であっても例外はthrow されず、default を返す。
val map = mapOf("key" to 1)
map["key"] // 1
40. zip
2つのコレクションをマージして新しいコレクションを作成する。
デフォルトでは、作成された新しいコレクションは、同じインデックスの要素をペアとしている。
また、どのような構造にするかを定義することもできる。
val stringList = listOf("a", "b", "c")
val numList = listOf(1, 2, 3, 4)
stringList zip numList // [(a, 1), (b, 2), (c, 3)]
stringList.zip(numList) { a, b -> "$a$b" } // [a1, b2, c3]
41. getOrElse
コレクションの要素への安全なアクセスを提供する事ができる。
このメソッドは、インデックスを受け取る。インデックスがコレクションの範囲外の場合、デフォルト値を返す。
val list = listOf(0, 2, 4)
list.getOrElse(1) { 40} // 2
list.getOrElse(10) { 40 } // 40
上記であれば、40がデフォルト値となっている。そのため、範囲外のインデックスが与えられた場合、40が返される。
42. let
Kotlin のスタンダードライブラリである。スコープやNullチェックで使用できる。
let は、与えられたブロックのコードを実行する。そして、最後の式の結果をリターンする。
このオブジェクトは、参照(it)もしくはカスタム名によってアクセスする事ができる。
val empty = "test".let {
customPrint(it)
it.isEmpty()
}
// false
fun printIfBothNonNull(strOne: String?, strTwo: String?) {
strOne?.let { firstString ->
strTwo?.let { secondString ->
customPrint("$firstString : $secondString")
}
}
}
}
printIfBothNonNull("first", "second") // first:second
43. run
let のように、run は、別にスコープを持つスタンダードライブラリである。
基本的には、let と同じである。
44. with
引数のメンバーに簡潔にアクセスする事ができるようになる。
with (person) {
println("$name, $age")
}
println("$person.name , $person.age") // 上記と同じ
45. apply
オブジェクトのコードブロックを実行し、そのオブジェクト自身を返す。
val jake = Person()
val stringPerson = jake.apply {
name = "jake"
age = 10
about = "developer"
}.toString()
name = "jake" は、jake.name = "jake" と同じ。apply で返されるのがオブジェクトである。そのため、チェーンメソッドで操作を続ける事ができる。
46. also
apply のように動作する。
与えられたブロックを実行し、オブジェクトを返す。
ログを出したい時のように、追加のアクションを加えたい時とかに使用する。
val jake = Person("jake", 30, "developer")
.also {
createLog(it)
}
}
47. Delegation Pattern
Kotlin は、Delegation Pattern をネイティブレベルでサポートしている。また、定型的なコードを書く必要がなく実装する事ができる。
interface SoundBehavior {
fun makeSound()
}
class ScreamBehavior(val n : String): SoundBehavior {
override fun makeSound() = println("${ n.toUpperCase }")
}
class behavior(n: String): SoundBehavior by ScreamBehavior(n)
behavior クラスでは、メソッドを実装していない。by キーワードを用いて、ScreamBehavior クラスをDelegate しているため、ScreamBehavior クラスのメソッドが定義されている事になる。
48. Delegate Properties
Kotlin は、delegated properties を提供する。これは、get メソッドとset メソッドをオブジェクトに委任する。
その委任オブジェクトは、getValue メソッドを持っているべきである。
そして、変更可能な properties であるため、setValue でセットする必要もある。
class Exam {
var p: String by Delegate()
override fun toString() = "Exmaple class"
}
class Delegate() {
operator fun getValue(thisRef: Any?, prop: KProperty<*>): String {
return "$thisRef, thank you for delegating '${prop.name}' to me"
}
operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: String {
println("$value has been assigned to ${prop.name} in $thisRef")
}
}
}
fun main() {
val e = Exam()
println(e.p)
e.p = "new set"
}
e.p でgetValue メソッドが呼ばれる。そして、e.p = "new set" でsetValue が呼ばれる。
49. Standard Delegates
Kotlin には、lazy やobservable のようなdelegatesで使用する便利なメソッドがたくさんある。
class LagySample {
init {
println("created!")
}
val lazyStr: String by lazy {
println("computed")
"my lazy"
}
}
fun main() {
val s = LazySample()
println("lazyStr = ${ s.lazyStr }") // my lazy
}
50. smart Casts
Kotlin では、以下の場合、自動的にキャストされる。
- Null を許容する型からNull を許容しない型へのキャスト
- 親の型から子の型へのキャスト