kotlin基本構文-パッケージ,継承,final修飾子,abstract,多態性,ダウンキャストとSmart cast,インターフェース

参考にしている書籍が薄すぎて悲しくなるが、サクサクっと読み進めてみる。
やはりJavaだからかクラス関係の記述が多いな。

パッケージのインポート

kotlinのインポート構文はJava相当の書き方ができる。
パッケージfoo.barに含まれるgooクラスをインポートする書き方と、
foo.barに含まれる全てのクラスをインポートする(static import)書き方は以下。


import foo.bar.goo
import for.bar.*

kotlinはパッケージのトップレベルにクラスだけでなく関数を置けるので、
import構文も関数を指定することができる。
foo.barパッケージのトップレベルにあるgetValue()をインポートする書き方は以下。


import foo.bar.getValue

別パッケージの同一クラスをimportする際には、
Javaでは完全修飾して書かないといけないが、kotlinではas予約語を使って別名でインポートすれば良い。


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。


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()で基底クラスのコンストラクタを呼び出す。


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に戻す責任があるということ。
派生は悪、という宣言。。


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予約語を使う。
指定位置は予想の通りで特別な印象はない。


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

多態性と異種リスト

多態性と異種リストのサンプル。何も難しいことはない。


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 = 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のダウンキャストとなっている。


open class Animal()
{
}

class Dog() : Animal()
{
    fun how()
    {
        println("howling")
    }
}
class Cat() : Animal()
{
    fun groom()
    {
        println("grooming")
    }
}
var animals:Array = 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を実装できる。


interface Biological {
    fun breath()
    fun sleep() {
        println("goo")
    }
}
interface Mammalian {
    fun nursing()
}

fun main(args: Array) {

    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予約語を使う。


interface Biological {
    fun breath() { println("foo") }
}
interface Mammalian {
    fun breath() { println("boo") }
}

fun main(args: Array) {

    open class Animal() : Biological, Mammalian
    {
        override final fun breath() {
            super.breath()
            super.breath()
        }
    }
    var animal = Animal()
    animal.breath()
}