列挙型を含むenumクラスは、ごく簡単に言えば「入れることができる値を制限したクラス」です。
例えば“電気”、“ガス”、“水道”はどれもString型ですが、この3つだけを含んだクラスには他の値を入れることができなくなります。
しかし列挙型の働きはこんな単純なものだけではありません。今回は、
- 列挙型、enumクラスの定義方法
- 列挙型とはどんな型なのか?
- 列挙型の使い方
についてお話したいと思います。
列挙型を定義する
基本的な定義方法
まずは簡単な列挙型を定義していきます。今回は支払い方法に関するクラスです。
enum class Payment{
//定数であるため、大文字表記することが多い
CASH, CREDIT, LINEPAY
}
列挙型を含むクラスは「enum class」で始め、値をその中に含めます。このクラスのインスタンスを変数に代入してみましょう。インスタンスは型名.値
で生成できます。
fun main() {
val a: Payment = Payment.CASH
println(a)
}
//CASH
これでPayment型はCASH、CREDIT、LINEPAYの3つの値しか入ることができない型となりました。
このページではこれら1つ1つの値を「列挙型」、列挙型を含むクラスを「enumクラス」として区別します。
コンストラクタとメソッドを追加する
enumクラスにはコンストラクタ引数やメソッドを定義することも可能です。
//コンストラクタの書式は通常通り
enum class Payment(val rate: Double){
CASH(1.0), //1つ1つの値は列挙型。「列挙子」とも呼ぶ
CREDIT(0.99),
//列挙型以外のメンバがある場合、列挙型の終わりを示すセミコロンが必要
LINEPAY(0.95);
//enumクラスのメソッド
fun calcPayment(x: Int): Int{
val sum = x * this.rate
return sum.toInt()
}
}
最後の列挙型の末尾にはセミコロンを付ける
基本的な記述方法は通常のクラスと大きくは変わりませんが、7行目のLINEPAY(0.95);
にセミコロンが付いていることに注目してください。
これは「この値が列挙型の最後の値です」という印。
enumクラスが列挙型のみで構成されている場合は不要ですが、今回のクラスにはメソッドがその後に続いて定義されています。
クラスの中に列挙型以外のメンバが含まれる場合、最後の列挙型の末尾にはセミコロンが必須となります。
コンストラクタは内部で呼び出される
もう1つの注意点はコンストラクタです。enumクラスのコンストラクタは、あくまでクラス内部で呼び出されるという点に注意しましょう。
各列挙型を見てみると、
通常のクラスとは違い、クラス内部でインスタンスが生成されていることが分かります。
プロパティやメソッドの呼び出し方法
コンストラクタによって初期化されたプロパティ(ここではrate
)は、enumクラスではなく各列挙型のプロパティとなります。
呼び出しはenumクラス名.列挙型名.プロパティ名
です。
println(Payment.CREDIT.rate) //0.99
メソッドも同じようにenumクラス名.列挙型名.メソッド名()
で呼び出します。
println(Payment.CREDIT.calcPayment(19800)) //19602
calcPayment
の呼び出しでは「19800」というInt型を受け取り、19800に列挙型のrateプロパティを掛けた数値を算出し、それによって支払い方法に応じた「実際の支払額」を出力しています。
列挙型は無名クラスである
列挙型はそれ自体が無名クラスであると言えます。今度はenumクラスではなく列挙型に、直接メソッドを定義してみましょう。
enum class Payment(val rate: Double){
CASH(1.0){
//enumクラスの抽象メソッドをオーバーライド
override fun calcPayment(x: Int): Int{
val sum = x * rate
return sum.toInt()
}
//toStringをオーバーライド
override fun toString(): String{
return "現金払い"
}
},
//(CREDITとLINEPAYは省略)
//Paymentクラスのメソッドは抽象メソッドとする
abstract fun calcPayment(x: Int): Int
}
このようにして定義した列挙型固有のメソッドも、先程と同じようにenumクラス名.列挙型名.メソッド名()
で呼び出すことができます。
enumクラスはただ単に複数の値を内包したクラスではなく、複数のクラスを内包し、さらにそのスーパークラスであるかのように振る舞います。これがenumクラスや列挙型の大きな特徴です。
- enumクラスのコンストラクタ引数は、内部の列挙型を初期化するために使われる
- enumクラスに定義したメソッドは、そのインスタンスである列挙型から呼び出す
- 列挙型はそれ自体がクラスであり、enumクラスはそのスーパークラスのようなもの
- enumクラスは抽象メンバを持つことができ、列挙型はそれをオーバーライドできる
列挙型の制限
このように特殊な構造のenumクラスですが、それだけに制限もあります。
他のクラスを継承できない
enumクラスは他のクラスを継承することができません。
class Example
enum class Payment(val rate: Double): Example{...}
//Enum class cannot inherit from classes
しかしインターフェイスを継承し、そのメソッドをオーバーライドすることは可能です。
interface Example{
fun some()
}
enum class Payment(val rate: Double): Example{...}
その場合は最終的にenumクラス本体か、内包する全ての列挙型でそのメソッドをオーバーライドする必要があります。
スーパークラスになれない
enumクラスのコンストラクタは、publicであったとしても他のクラスからは呼び出せません。そのため他のクラスでenumクラスを継承することはできません。
class Example(rate: Double): Payment(rate){}
enum class Payment(val rate: Double){...}
//Cannot access '<init>': it is private in 'Payment'
列挙型のプロパティとメソッド
enumクラスや列挙型に「標準で用意されている」プロパティ、メソッドがこちらです。数は多くありませんが、ordinal
やvalues()
は押さえておきたいところ。
プロパティ/メソッド名 | 機能 |
---|---|
Enum.SOME.name | SOMEをString型として出力 |
Enum.SOME.ordinal | SOMEがenumクラスの何番目のインスタンスかをIntで出力 |
Enum.SOME.toString() | SOMEに対しtoStringメソッドを使用する |
Enum.valueOf(“SOME”) | 同上 |
Enum.values() | enumクラスの列挙型を配列(Array)で出力 |
enumクラスの使い方
values()でループ処理
今回作成したenumクラスと列挙型を実際に使用してみます。まずはforEach
を用いたループ処理です。
fun main() {
//values()の型を確認
println(Payment.values().javaClass.kotlin)
Payment.values().forEach {
println("${it.ordinal}番目は${it.name}、String表現は${it.toString()}")
}
}
//class kotlin.Array
//0番目の列挙型はCASH、String表現は現金払い
//1番目の列挙型はCREDIT、String表現はクレジット払い
//2番目の列挙型はLINEPAY、String表現はLINE Pay払い
列挙型をwhenで使う
列挙型の利用法としてはメジャーなwhen式。ここでは簡易的な対話型プログラムに列挙型を使用しています。
fun main() {
print("お支払い方法をどうぞ:")
val input = requireNotNull(readLine())
when(input){
"CASH" -> println("${Payment.valueOf("CASH")}ですね")
"CREDIT" -> println("${Payment.valueOf("CREDIT")}ですね")
"LINEPAY" -> println("${Payment.valueOf("LINEPAY")}ですね")
else -> println("じゃいいです")
}
}
//実行結果1
お支払い方法をどうぞ:LINEPAY
LINE Pay払いですね
//実行結果2
お支払い方法をどうぞ:line
じゃいいです
列挙型をインポートして使う
これまでPayment.CASH.calcPayment(19800)
とenum型から表記してきましたが、Kotlinのインポート機能を利用することでenum型の表記を省略することができます。
//enumクラスの定義
enum class Payment(val rate: Double) {
CASH(1.0),
CREDIT(0.99),
LINEPAY(0.95);
fun calcPayment(x: Int): Int {
val sum = x * rate
return sum.toInt()
}
}
なおインポートは同じファイルからでもかまいません。上記のenumクラスをインポートしてみましょう。
//enum型から全てのインスタンスをインポート
import Payment.*
fun main() {
//main関数内では「Payment」の表記を省略
println(CASH.rate)
println(LINEPAY.calcPayment(3000))
}
//実行結果
1.0
2850