オブジェクト指向プログラミングの3つのキーワードで最も理解しやすいのが「継承」ですが、トピックとしてはかなりのボリュームがあります。
今回はKotlinの継承その1として、スーパークラス(親クラス)とサブクラス(子クラス)の定義方法についてお話していきます。
スーパークラスとサブクラスを作成する
スーパークラスの準備
まずはクラスを準備します。皆さんが会社を設立するプランを練っているとして、Companyクラスを定義しましょう。
- 会社の名前と資金を引数に取り、名前はプライマリコンストラクタで初期化する
- 何かを売るための
sell
メソッドを持つ
class Company(val name: String, _fund: Int){
var fund = _fund
fun sell(): Int{
fund += 10
return fund
}
}
こんなもんでいいかな?
いや待てよ…従業員も雇わなきゃいけないし、機材も揃えなきゃいけない。あとあれもこれも…。
もっと具体的に決めていきたい? ならサブクラスを作るのはどうでしょう社長。
サブクラスは簡単に作成できます。そのための準備は、このCompanyクラスに「open」を付け足すだけです。
open class Company(val name: String, _fund: Int){...}
これでCompanyクラスは継承を許すスーパークラスとして定義されたことになります。ではこのクラスを継承したサブクラスを作っていきましょう。
サブクラスの定義
Companyクラスと同じファイルに記述していきます。
class MyCompany(name: String, _fund: Int): Company(name, _fund)
//コロンの右辺で、スーパークラスのコンストラクタを呼び出す
これでCompanyクラスを継承したMyCompanyクラスが定義されました。このクラスはスーパークラスのプロパティ、メソッドを引き継いだクラスです。一度インスタンス化して使ってみることにします。
fun main() {
val sony: Company = MyCompany("SONY", 1000)
println(sony.name) //SONY
println(sony.fund) //1000
sony.sell()
println(sony.fund) //1010
}
main関数内ではサブクラスをインスタンス化し、プロパティとメソッドが正しく使用できています。
変数sonyの型はCompany型であることに注目しましょう。Companyクラスを継承したクラスは、コンストラクタの引数が同じであればCompany型としてインスタンス化することができます。もちろんMyCompany型でもかまいません。
・サブクラス(子クラス)はスーパークラスのプロパティ、メソッドを全て引き継ぐ
・サブクラスは引数が同じであればスーパークラスの型としてインスタンス化できる
記述のルール
これでスーパークラスとサブクラスが作成できましたが、その記述方法にはコンストラクタのパラメータ設定によって、いくつかの異なるルールがあります。ここでチェックしておきましょう。
両方共にコンストラクタの引数が無い場合:
open class Company{...}
class MyCompany: Company(){...} //スーパークラスに()を付けて呼び出す
これは最もシンプルな例。特に難しいところはありません。
両方共にコンストラクタの引数がある場合(上記の例と同じ):
型名を記述する必要があるのか無いのかは若干ややこしい部分かもしれません。ここでチェックしておきましょう。
open class Company(val name: String, _fund: Int){...}
//初期化するパラメータの型名は省略不可
class MyCompany(name: String, _fund: Int, some: Float): Company(name, _fund)
//スーパークラスに渡す引数(ここではname/_fund)の型名は省略不可(val/varは記述しない)
//コロンの右辺では型名を省略
//上記のように、サブクラスで追加の引数を取ることも可能
追加の引数を取る場合はスーパークラスの型としてインスタンスを生成することはできません。
可能であればコンストラクタの引数は共通にしておき、クラス内部で独自のプロパティを定義する方が、後々使いやすいクラスになるでしょう。
class MyCompany(name: String, _fund: Int) : Company(name, _fund) {
val some = 2.84
}
スーパークラスには引数があり、サブクラスには無い場合:
open class Company(val name: String, _fund: Int){...}
class MyCompany: Company{
constructor(name: String,_fund: Int):super(name, _fund){
fund = _fund + 100 //インスタンス生成時に何らかの処理をする
}
}
//セカンダリコンストラクタで初期化する。
//セカンダリコンストラクタの場合、スーパークラスは「super」で参照する
スーパークラスの引数を、サブクラスのプライマリコンストラクタでは取らないことも考えられます。その場合、セカンダリコンストラクタを使うか、後に出てくるinitブロックを使うことになります。
スーパークラスのコンストラクタを呼び出す処理は他と共通ですが、ここではクラス名ではなくsuper
を使っている点に注意しましょう。
しかし上記の例はあくまで例外的な記述方法です。プライマリコンストラクタとinitブロックを使う選択がベターかと思います。
class MyCompany(name: String, _fund: Int) : Company(name, _fund) {
init { //初期化処理はinitブロックで行う
fund = _fund + 100
}
}