KotlinDive

プログラミング言語 Kotlin についての入門ブログです。

クラス(コンストラクタ)

クラスの宣言とクラスメンバ

クラスは「class」キーワードで宣言します。
クラスは、メンバと呼ばれる以下の要素で構成されます。

メンバ 内容
コンストラク
イニシャライザブロック
インスタンスの初期化
関数 Javaでいうメソッド
プロパティ Javaでいうメンバにゲッター/セッターなどの機能をもたせたもの
ネスト/インナークラス クラス内で宣言されたクラス
オブジェクト宣言 シングルトンなどを作成するときに利用

コンストラク

クラスにはプライマリコンストラクタとセカンダリコンストラクタという2種類があります。
いずれも必須ではありません。

プライマリコンストラク

プライマリコンストラクタは「class」キーワードの後ろの方、 つまりクラスヘッダーに「constructor」をつけて記述します。


fun main(args: Array) {
    val student = Student("suzuki", 80.0)
}

              /* この部分がプライマリコンストラクタ      */
class Student constructor(name: String, score: Double) {
}

なお、クラスのボディ(プロパティなど「{ }」の中身の部分)が不要な場合は、「{ }」を省略できます。


fun main(args: Array) {
    val student = Student("suzuki", 80.0)
}

class Student constructor(name: String, score: Double)

プライマリコンストラクタの「constructor」部分は省略可能です。 逆に言うと、「constructor」が必要なのは、ヴィジビリティ修飾子(publicなど)やアノテーション(@Injectなど)をつけたい場合です。


fun main(args: Array) {
    val student = Student("suzuki", 80.0)
    val school = School("Kotrin School")
}

class Student(name: String, score: Double)

class School public constructor(name: String)

さて、この「プライマリコンストラクタ」の使い方ですが、コンストラクタの宣言部にはパラメータを記述することができるだけで、 何かの処理を行うコードを記述することはできません。
では、何かしらの処理をしたい場合どうするのかというと、「init」キーワードで指定された「イニシャライザブロック」にコードを記述します。


fun main(args: Array) {
    val student = Student("suzuki", 80.0)
}

class Student(name: String, score: Double) {
    init {
        println("initializer")
        println("name = ${name}")
        println("score = ${score}")
    }
}

initializer
name = suzuki
score = 80.0

また、プロパティイニシャライザとしても、プライマリコンストラクタの変数を利用できます。


fun main(args: Array) {
    val student = Student("suzuki", 80.0)

    println("name length = ${student.nameLength}")
    println("description = ${student.description}")
}

class Student(name: String, score: Double) {
    val nameLength = name.length
    val description = "name: ${name} score: ${score}"

    init {
        println("initializer")
        println("name = ${name}")
        println("score = ${score}")
    }
}

initializer
name = suzuki
score = 80.0
name length = 6
description = name: suzuki score: 80.0

また、イニシャライザブロックは分割することもできます。 分割した場合は、単純に上にあるものから順番に実行されていきます。
イニシャライザブロックの中でもプロパティを参照することはできますが、そのイニシャライザブロックより前に宣言されていなければなりません。


fun main(args: Array) {
    val student = Student("suzuki", 80.0)
}

class Student(name: String, score: Double) {
    init {
        println("First Block")
        println("name = ${name}")
        println("score = ${score}")
    }

    val nameLength = name.length

    init {
        println("Second Block")
        println("nameLength = ${nameLength}")

        // 以下のコードはビルドエラー
        // println("descripton = ${description}")
    }

    val description = "name: ${name} score: ${score}"

    init {
        println("Third Bllock")
        println("descripton = ${description}")
    }
}


First Block
name = suzuki
score = 80.0
Second Block
nameLength = 6
Third Bllock
descripton = name: suzuki score: 80.0

セカンダリコンストラク

また、クラスボディ内でconstructorキーワードを指定することで、セカンダリコンストラクタを宣言することもできます。
プライマリコンストラクタと違い、セカンダリコンストラクタは複数個持つこともできます。


fun main(args: Array) {
    val school = School()
    val suzuki = Student(school)
    val tanaka = Student()

    println("student count = ${school.students.count()}")
}

class Student {
    constructor(school: School) {
        school.students.add(this)
    }

    constructor() {
    }
}

class School {
    public val students = arrayListOf()
}

student count = 1

プライマリコンストラクタとセカンダリコンストラクタの両方がある場合、 セカンダリコンストラクタはプライマリコンストラクタに処理を委譲する(プライマリコンストラクタを呼び出す)必要があります。
また、プライマリコンストラクタを直接委譲しなくても、プライマリコンストラクタに委譲している他のセカンダリコンストラクタに委譲しても大丈夫です。
他のコンストラクタへの委譲は「this」キーワードを利用して行います。


fun main(args: Array) {
    val school = School()

    val sato = Student()
    println(sato.nameKey)
    println()
    val suzuki = Student(school)
    println(suzuki.nameKey)
    println()
    val tanaka = Student("tanaka", school)
    println(tanaka.nameKey)
    println()

    println("student count = ${school.students.count()}")
}

class Student(name: String) {
    public val nameKey = name.toUpperCase()

    init {
        println("initializer block")
    }

    constructor(): this("None") {
        // thisでプライマリコンストラクタに委譲している
        println("secondary constructor: 1")
    }

    constructor(school: School): this() {
        // thisでセカンダリコンストラクタ1に委譲している
        println("secondary constructor: 2")
    }

    constructor(name: String, school: School): this(name) {
        // thisでプライマリコンストラクタに委譲している
        println("secondary constructor: 3")
        school.students.add(this)
    }

}

class School {
    public val students = arrayListOf()
}

initializer block
secondary constructor: 1
NONE

initializer block
secondary constructor: 1
secondary constructor: 2
NONE

initializer block
secondary constructor: 3
TANAKA

student count = 1