Skip to content

Latest commit

 

History

History
471 lines (357 loc) · 16.1 KB

classes.md

File metadata and controls

471 lines (357 loc) · 16.1 KB
layout title
reference
クラス

クラス

Kotlinでのクラスは、class{: .keyword }キーワードを使用して宣言されます。

class Person { /*...*/ }

クラス宣言はクラス名、クラスヘッダ(型パラメータや主コンストラクタ等の指定)、 そして中括弧で括られたクラス本体で構成されます。 ヘッダと本体は両方とも必須ではありません。 クラスに本体がない場合は、中括弧を省略することができます。

class Empty

コンストラクタ

Kotlinのクラスは、 プライマリコンストラクタ を持ち、 また1つまたは複数の セカンダリコンストラクタ を持つ場合もあります。 プライマリコンストラクタは、クラスのヘッダーとして宣言され、 クラス名(それに型パラメータをつけることもできます)の後に続きます。

class Person constructor(firstName: String) { /*...*/ }

プライマリコンストラクタがアノテーションや可視性修飾子を持っていない場合は、 constructor{: .keyword }のキーワードを省略することができます。

class Person(firstName: String) { /*...*/ }

プライマリコンストラクタはクラスのインスタンスとクラスヘッダにあるプロパティを初期化します。 プライマリコンストラクタは、どんな実行可能コードも含めることはできません。 オブジェクト生成時に何らかのコードを実行したい場合は、 クラス本体の中の初期化ブロック(initalizer blocks) を使います。 初期化ブロックはinit{: .keyword }キーワードの後に中括弧を続けて宣言します。 実行したコードをなんでもこの中括弧の中に書く事が出来ます。

インスタンスの初期化時には、初期化ブロックはクラス本体に現れるのと同じ順番で実行され、 その間にあるプロパティの初期化が挟まる形になります。

{% capture initializer-block %} //sampleStart class InitOrderDemo(name: String) { val firstProperty = "最初のプロパティ: $name".also(::println)

init {
    println("最初の初期化ブロックによる $name のプリント")
}

val secondProperty = "二番目のプロパティ: ${name.length}".also(::println)

init {
    println("二番目の初期化ブロックによる ${name.length} のプリント")
}

} //sampleEnd

fun main() { InitOrderDemo("hello") } {% endcapture %} {% include kotlin_quote.html body=initializer-block %}

プライマリコンストラクタのパラメータは初期化ブロックで使う事が出来ます。 それらのパラメータはまた、クラス本体に宣言されているプロパティの初期化でも使う事が出来ます:

class Customer(name: String) {
    val customerKey = name.uppercase()
}

Kotlinには、プロパティの宣言と初期化を主コンストラクタから行うための簡潔な構文があります:

class Person(val firstName: String, val lastName: String, var age: Int)

この定義方法では、クラスのプロパティのデフォルト値を含める事も出来ます。

class Person(val firstName: String, val lastName: String, var isEmployed: Boolean = true)

クラスのプロパティを宣言する時は、トレーリングカンマを使う事が出来ます:

class Person(
    val firstName: String,
    val lastName: String,
    var age: Int, // trailing comma
) { /*...*/ }

通常のプロパティと同様に、プライマリコンストラクタの中で定義されるプロパティは可変値(ミュータブル) ( var{: .keyword } ) または読み取り専用(イミュータブル) ( val{: .keyword} ) で宣言することができます。

もしコンストラクタがアノテーションや可視性修飾子を持つ場合は、 constructor{: .keyword } キーワードが必要で修飾子はその前に置かれます:

class Customer public @Inject constructor(name: String) { ... }

詳細については、可視性修飾子を参照してください。

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

クラスは、 constructor{: .keyword } プレフィクスを用いて セカンダリコンストラクタ を宣言することができます:

class Person(val pets: MutableList<Pet> = mutableListOf())

class Pet {
    constructor(owner: Person) {
        owner.pets.add(this) // adds this pet to the list of its owner's pets
    }
}

もしクラスがプライマリコンストラクタを持つなら、それぞれのセカンダリコンストラクタは直接的または他のセカンダリコンストラクタを介して間接的に、プライマリコンストラクタへ委譲する必要があります。 同クラスの他コンストラクタへの委譲は this{: .keyword } キーワードを用いて行います:

class Person(val name: String) {
    val children: MutableList<Person> = mutableListOf()
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

初期化ブロックの中のコードは実質的にはプライマリコンストラクタの一部となります。 プライマリコンストラクタへの委譲はセカンダリコンストラクタの最初の文が実行される直前に行われます。 つまり、セカンダリコンストラクタの本体が実行される前にすべての初期化ブロックとプロパティの初期化が実行されるという事です。

クラスがプライマリコンストラクタを持っていなくても、 委譲は暗黙的に行われていて、初期化ブロックはやはり実行されます:

{% capture implicit-primary-constructor %} //sampleStart class Constructors { init { println("Initブロック") }

constructor(i: Int) {
    println("コンストラクタ $i")
}

} //sampleEnd

fun main() { Constructors(1) } {% endcapture %} {% include kotlin_quote.html body=implicit-primary-constructor %}

もし非抽象クラスが何もコンストラクタ(プライマリ、セカンダリ共に)を宣言しなければ、プライマリコンストラクタが引数無しで生成されます。その際のコンストラクタの可視性はpublicになります。

もしpublicなコンストラクタを望まないならば、空のプライマリコンストラクタをデフォルトでない可視性で宣言する必要があります。

class DontCreateMe private constructor() { /*...*/ }

JVMでは、プライマリコンストラクタの全ての引数がデフォルト値を持つなら、 コンパイラは引数無しコンストラクタを追加で生成し、そのコンストラクタはデフォルト値を使用します。 これにより、JacksonやJPAのように引数が無いコンストラクタを通してクラスのインスタンスを作るようなライブラリを、 Kotlinで使いやすくなります。

class Customer(val customerName: String = "")

{: .note}

クラスのインスタンス生成

クラスのインスタンスを生成するには、コンストラクタを普通の関数のように呼び出せば良いです:

val invoice = Invoice()

val customer = Customer("Joe Smith")

Kotlinに new{: .keyword } キーワードはありません。

{: .note}

ネストされたクラス、インナークラス、そして無名インナークラスの生成時の過程はネストされたクラスに記述されています。

クラスメンバ

クラスは以下を含めることができます:

継承

クラスは他のクラスから継承出来て、継承ヒエラルキー(inheritance hierarchies)を形成します。 Kotlinに置ける継承についてより詳しく学ぶ

抽象クラス

クラスは abstract{: .keyword } を使用して抽象クラスとして宣言する事が出来、 そのメンバの幾つか(すべてでも)もabstract{: .keyword }を用いて抽象メンバとして宣言出来ます。 抽象メンバはそのクラス内に実装を持ちません。 抽象クラスや抽象関数にopenアノテーションを付ける必要はありません。

abstract class Polygon {
    abstract fun draw()
}

class Rectangle : Polygon() {
    override fun draw() {
        // 四角を描く
    }
}

(訳注:Polygonは多角形でRectangleは長方形)

非抽象でopenなメンバを抽象メンバでオーバライドすることもできます。

open class Polygon {
    open fun draw() {
        // なんらかのデフォルトの多角形の描画メソッド
    }
}

abstract class WildShape : Polygon() {
    // WildShapeを継承するクラスはデフォルトのPolygonのdrawメソッドを使わずに、
    // 自分自身のdrawメソッドを提供する必要がある
    abstract override fun draw()
}

コンパニオンオブジェクト (Companion Objects)

Kotlinでは、JavaやC#とは異なり、クラスはstaticメソッドを持ちません。ほとんどの場合、代替として、パッケージレベルの関数を使用することが推奨されています。

もしクラスインスタンス無しで呼べるがクラス内部へのアクセスが必要な関数(例えばファクトリメソッド)を書く必要があれば、 そのクラスの中で object宣言 のメンバとして書くことができます。

より具体的に、コンパニオンオブジェクト をクラス内で宣言した場合、 クラス名を識別子として、そのメンバにアクセスすることが出来ます。