参考にしている書籍が薄すぎて悲しくなるが、サクサクっと読み進めてみる。
やはりJavaだからかクラス関係の記述が多いな。
パッケージのインポート
kotlinのインポート構文はJava相当の書き方ができる。
パッケージfoo.barに含まれるgooクラスをインポートする書き方と、
foo.barに含まれる全てのクラスをインポートする(static import)書き方は以下。
1 2 |
import foo.bar.goo import for.bar.* |
kotlinはパッケージのトップレベルにクラスだけでなく関数を置けるので、
import構文も関数を指定することができる。
foo.barパッケージのトップレベルにあるgetValue()をインポートする書き方は以下。
1 |
import foo.bar.getValue |
別パッケージの同一クラスをimportする際には、
Javaでは完全修飾して書かないといけないが、kotlinではas予約語を使って別名でインポートすれば良い。
1 2 3 4 5 6 7 |
import foo.bar.getValue as barGetValue import foo.hoge.getValue as hogeGetValue fun hoge() { println(barGetValue(10)) println(hogeGetValue(20)) } |
継承
kotlinのクラスはデフォルトでfinal。デフォルトで継承できない。
open修飾子をつけることでfinalが外れ、継承できるようになる。
オブジェクト指向モデリングを少しかじると、やたら継承関係を作りたくなる参考書が多くて、
継承関係でないのにやたら継承関係にする人が多いと聞いたな…。
継承はextendsではなく「:」。
派生クラスのプライマリコンストラクタで基底クラスのプライマリコンストラクタを呼んでいる。
関数(kotlinはクラスのメンバ関数をメソッドと呼ばないで関数と呼ぶらしい)のデフォルトもfinal。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
open class Person(private var name: String = "Tom") { open public fun sayHello() { println("My name is ${name}") } } class LuckyPerson(private var name: String = "Tom", private var happiness: Int = 100) : Person(name) { override public fun sayHello() { println("My name is ${name}, my happiness is ${happiness}") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 |
派生クラスがプライマリコンストラクタを持たない場合、
基底クラスのコンストラクタを呼ぶ書き方が別にある。
super()で基底クラスのコンストラクタを呼び出す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
open class Person(private var name: String = "Tom") { open public fun sayHello() { println("My name is ${name}") } } class LuckyPerson : Person { var name: String var happiness: Int constructor (name: String, happiness: Int) :super() { this.name = name this.happiness = happiness } override public fun sayHello() { println("My name is ${this.name}, my happiness is ${happiness}") } } var person = LuckyPerson("Tom",200) person.sayHello() |
メンバ関数のデフォルトがfinalということは、
openなメンバ関数をoverrideした後、finalに戻す責任があるということ。
派生は悪、という宣言。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
open class Person(private var name: String = "Tom") { open public fun sayHello() { println("My name is ${name}") } } class LuckyPerson(private var name: String = "Tom", private var happiness: Int = 100) : Person(name) { final override public fun sayHello() { println("My name is ${name}, my happiness is ${happiness}") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 |
抽象クラス、抽象メソッド
抽象クラス、抽象メソッドはabstract予約語を使う。
指定位置は予想の通りで特別な印象はない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
abstract class Person(private var name: String = "Tom") { abstract public fun sayHello() } class LuckyPerson(private var name: String = "Tom", private var happiness: Int = 100) : Person(name) { final override public fun sayHello() { println("My name is ${name}, my happiness is ${happiness}") } } var person = LuckyPerson() person.sayHello() // My name is Tom, my happiness is 100 |
多態性と異種リスト
多態性と異種リストのサンプル。何も難しいことはない。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
abstract class Animal() { abstract fun sayHello() } class Dog() : Animal() { final override fun sayHello() { println("bow") } } class Cat() : Animal() { final override fun sayHello() { println("mew") } } var animals:Array<Animal> = arrayOf(Dog(),Cat()) animals.forEach { animal -> animal.sayHello() } // bow mew |
ダウンキャストとSmart Cast
派生クラスの固有メンバ関数を呼び出すとき、インスタンスのis a関係をチェックしてから
派生クラスにダウンキャストして呼ぶのが普通だと思っていたけども、
kotlinは、is a関係のチェックとダウンキャストを自動的に行ってくれる。
以下の通り、animalインスタンスのis aチェックを行ったあと、
animal.how()および、animal.groom()のコンテキストでは、
animalがそれぞれDog,Catのダウンキャストとなっている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
open class Animal() { } class Dog() : Animal() { fun how() { println("howling") } } class Cat() : Animal() { fun groom() { println("grooming") } } var animals:Array<Animal> = arrayOf(Dog(),Cat()) animals.forEach { animal -> if (animal is Dog) { animal.how() } // Smart cast to Dog else if (animal is Cat) { animal.groom() } // Smart cast to Cat } |
インターフェース
kotlinは多重継承をサポートしていない。
振る舞いを複数に分割しておいて必要な振る舞いを実装するモデル。
振る舞いはインターフェース(純粋仮想クラス)として書くようになっている。
インターフェースはabstract関数(ex. breath())だけでなくデフォルト実装(ex.sleep())を持てる。
当然、クラスは複数のinterfaceを実装できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
interface Biological { fun breath() fun sleep() { println("goo") } } interface Mammalian { fun nursing() } fun main(args: Array<String>) { open class Animal() : Biological, Mammalian { final override fun breath() { println("foo.") } final override fun nursing() { println("nursing.") } } var animal = Animal() animal.breath() animal.sleep() animal.nursing() } |
インターフェースの名前解決
kotlinではインターフェースがデフォルト実装を持てるので、
実装クラス側から定義済みのデフォルト実装を呼ぶケースがある。
もし、実装する複数のインターフェースが同じ名称を持つメンバ関数を持っていた場合、
競合する複数のメンバ関数から
どれを呼ぶのか解決する必要がある。
実装クラスからインターフェースを参照する場合には、super予約語を使う。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
interface Biological { fun breath() { println("foo") } } interface Mammalian { fun breath() { println("boo") } } fun main(args: Array<String>) { open class Animal() : Biological, Mammalian { override final fun breath() { super<Biological>.breath() super<Mammalian>.breath() } } var animal = Animal() animal.breath() } |