kotlin基本構文-クラス,プロパティ,アクセス修飾子,コンストラクタ

言語仕様に拘りがあるタイプではなさそうで、かなり平易。

クラスの定義

public,private修飾子を指定したメソッドを持つクラスを定義してみる。
インスタンス化はnewが要らないんだな。


class Person() {
   public fun sayHello() {
      println("hello,kotlin")
      sayYes()
   }
   private fun sayYes() {
      println("yes")
   }
}
val person = Person()
person.sayHello() // hello,kotlin  yes

気になるのは、アクセス修飾子が無いときの振る舞い。
デフォルトのアクセス修飾子はprivateではなくpublic。
Javaのデフォルトのアクセス修飾子は”package内のみ”だった。


class Person() {
     fun hogehoge() {
          println("hogehoge")
     }
}
val person =  Person()
person.hogehoge()

メンバ変数とプロパティ

Delphiで便利だったプロパティ。getter,setterを文法で解決する。
まずメンバ変数は以下の通り普通に定義できる。
これは後述するgetter,setterを省略した書き方。


    class Person2() {
        var name = "Ann"
        fun show() {
            println("My name is ${name}.")
        }
    }
    val person2 = Person2()
    person2.show()

ageというメンバ変数に対してsetterを定義する。
外部からageというメンバ変数に値を代入する際に定義したsetterが呼ばれる。
異常値の入力値チェックが出来たりする。
getter,setterを文法で解決したメンバ変数をプロパティと言ってるらしい。
getter,setterでメンバ変数を参照する際、selfを使わずfieldという予約語を使う様子。
backing fieldという名前が付いてる。


    class Person2() {
        var name = "Ann"
        var age = 10
            set (value) {
                if (value < 0) {
                    println("指定した値は不正です。")
                } else {
                    field = value
                }
            }
        fun show() {
            println("My name is ${name}. My age is ${age}")
        }
    }
    val person2 = Person2()
    person2.show() // My name is Ann. My age is 10
    person2.age = 20
    person2.show() // My name is Ann. My age is 20
    person2.age = -10 // 指定した値は不正です。
    person2.show() // My name is Ann. My age is 20

なお、プロパティを変数(var)ではなく定数(val)で定義すると、読み取り専用プロパティになる。


    class Person3() {
        val age = 20
    }
    var person3 = Person3()
    person3.age = 100 // Error:(39, 5) Kotlin: Val cannot be reassigned

プロパティの初期値を設定せず、コンストラクタでも代入しなかった場合はインスタンス化時にエラーとなる。
abstractクラスならOK。


    class Person4() {
        val age
        public fun say() {
            println("${age}")
        }
    }
    var person4 = Person4()
    person4.say() //Error:(42, 9) Kotlin: Property must be initialized or be abstract

プロパティのsetterにアクセス修飾子を指定できる。
public修飾子がついたプロパティのsetterにprivate修飾子をつけると、
クラス内部からはsetter経由で値を変更できるが、外部からは変更できなくなる。


class Person5() {
    var name = "Ann"
    var age = 10
    private set (value) {
        if (value < 0) {
           println("指定した値は不正です。")
        } else {
           field = value
        }
    }
    fun show() {
        println("My name is ${name}. My age is ${age}")
    }
}
val person5 = Person5()
person5.show() // My name is Ann. My age is 10
person5.age = 20 // Error:(34, 5) Kotlin: Cannot assign to 'age': the setter is private in 'Person5'

プライマリコンストラクタ

コンストラクタはinit予約語の後に書く。コンストラクタのパラメタはinitに書かずclassに書く。
コンストラクタのパラメタがclassに付いているのでinit以外でも使ってしまいそうになるがinitでしか使えない。
コンストラクタに渡した文字列を使ってメンバ変数を初期化してみる。


class Person(name: String) {
    var name: String
    init {
        this.name = name
        println("${this.name} initialized")
    }
    public fun sayHello() {
        println("say hello")
    }
}
val person =  Person("Tom")
person.sayHello()

この書き方は、constructor予約語が省略されたものであって、
constructor予約語を省略しない場合は以下のようになる。


class Person constructor(name: String) {
    var name: String
    init {
        this.name = name
        println("${this.name} initialized")
    }
    public fun sayHello() {
        println("say hello")
    }
}
val person =  Person("Tom")
person.sayHello()

コンストラクタの実行とメンバ変数の初期化を一度に書くのは以下。


class Person(var name: String) {
    init {
        println("${this.name} initialized")
    }
    public fun sayHello() {
        println("say hello")
    }
}
val person =  Person("Tom")
person.sayHello()

アクセス修飾子までコンストラクタに含められる。


class Person(private var name: String) {
    init {
        println("${this.name} initialized")
    }
    public fun sayHello() {
        println("say hello")
    }
}
val person =  Person("Tom")
person.sayHello()

さらにデフォルト値を含められる。


    class Person(private var name: String = "Tom") {
        init {
            println("${this.name} initialized")
        }
        public fun sayHello() {
            println("say hello")
        }
    }
    val person =  Person()
    person.sayHello()

セカンダリコンストラクタ

2つ以上のコンストラクタを書くときのやり方。
2つ目以降は順当にconstructor予約語を書いていく。
ほとんどの場合コンストラクタは1個だから1つ目のコンストラクタを簡易な方法でかけるようになっているのかな。

:thisというのは、プライマリコンストラクタを呼び出す書式。
ブロックの先頭に書くのではなく専用の書式がある。
セカンダリコンストラクタはプライマリコンストラクタを必ず呼ばなければならない。


class Person(var name: String = "Tom") {
    init {
        println("My name is ${name}")
    }
    constructor(): this("Alice") {}
    public fun sayHello() {
        println("say hello")
    }
}
val person =  Person()
person.sayHello()