S-JIS[2011-01-08/2011-04-15] 変更履歴

Scala列挙型

Scalaの列挙型(定数群の定義)のメモ。


定義方法

Javaではenumで列挙型を定義するが、ScalaではEnumerationクラスを継承したオブジェクトで定義する。

Scala Java相当 備考
object 列挙名 extends Enumeration {
  val 列挙子, 列挙子, … = Value
}
object E1 extends Enumeration {
  val A,B,C = Value
}
enum 列挙名 {
  列挙子, 列挙子, …
}
enum E1 {
  A,B,C
}
列挙子に代入しているValueは、Enumeration内で定義されているValueメソッドの複数回呼び出し
(これによって異なるインスタンスが返るので、列挙子同士を区別できる)
列挙子を区別する為に内部で採番される連番(ID)は0から開始する。
object E2 extends Enumeration(10) {
  val A,B,C = Value
}
  内部で採番される連番(ID)を指定した番号から始める例。
object E3 extends Enumeration {
  val A = Value(11)
  val B = Value(22)
  val C = Value(33)
}
  IDを個別に割り当てる例。
object E4 extends Enumeration {
  val A = Value("aaa")
  val B = Value("bbb")
  val C = Value("ccc")
}
object E4 extends Enumeration {
  val A = Value(11, "aaa")
  val B = Value(22, "bbb")
  val C = Value(33, "ccc")
}
object E4 extends Enumeration(
  "aaa", "bbb", "ccc"
) {
  val A,B,C = Value
}
  列挙子の名前を個別に割り当てる例。
object 列挙名 extends Enumeration {
  type 列挙名 = Value
  val 列挙子, 列挙子, … = Value
}
object E1 extends Enumeration {
  type E1 = Value
  val A,B,C = Value
}
enum 列挙名 {

  列挙子, 列挙子, …
}
enum E1 {

  A,B,C
}
typeで名前を付けておくと、importして使う際に便利。

列挙名に代入しているValueと列挙子に代入しているValueは
どちらも同じ名前だが、前者はクラスで後者はメソッド。
列挙名に代入しているValueは、Enumeration内で定義されているValueクラス。
列挙子に代入しているValueは、Enumeration内で定義されているValueメソッドの複数回呼び出し

使用方法

列挙型はオブジェクトなので、普通にオブジェクトとして使用する。

Scala Java相当 備考
object E1 extends Enumeration {
  val A,B,C = Value
}
scala> val e = E1.A
e: E1.Value = A

scala> if (e == E1.A) println("a")
a
enum E1 {
  A,B,C
}
final E1 e = E1.A;


if (e == E1.A) 〜
 
object E1 extends Enumeration {
  type E1 = Value
  val A,B,C = Value
}
scala> import E1._
import E1._

scala> val e = A
e: E1.Value = A

scala> def f(e:E1) = println(e)
f: (e: E1.E1)Unit
enum E1 {

  A,B,C
}
import static E1.*;


final E1 e = A;


public void f(E1 e) { 〜 }
列挙型オブジェクトのメンバーをimportすると
列挙子を接頭辞抜きで使用できるので便利。
また、列挙型オブジェクトの中で型名を定義しておくと
その型名で変数を宣言できるので便利。
e match {
  case A => println("aho")
  case B => println("boo")
  case C => println("chu")
}
e match {
  case A | B =>
    println("abu")
  case _ =>

}
switch (e) {
case A: System.out.println("aho");break;
case B: System.out.println("boo");break;
case C: System.out.println("chu");break;
}
switch (e) {
case A: case B:
  System.out.println("abu");break;
default:
  break;
}

列挙型オブジェクトのメソッド

列挙型はEnumerationを継承したオブジェクトであり、列挙子の実体はEnumeration#Valクラスなので、そのメソッドが呼び出せる。

  Scala Java相当 備考
列挙子の名前
e.toString()
e.toString
e.name()
 
列挙子のID(序数)
e.id
e.ordinal()
 
大小比較
e1.compareTo(e2)
e1 compare e2
e1 <  e2
e1 <= e2
e1 >  e2
e1 >= e2
e1.compareTo(e2)
IDによる比較。
列挙子取得(名前)
E.withName("name")
E.valueOf("name")
見つからない場合はjava.util.NoSuchElementExceptionが発生する。
列挙子取得(ID)
E.apply(n)
E(n)
  見つからない場合はjava.util.NoSuchElementExceptionが発生する。
列挙子一覧取得
E.values
E.values()
ID順に並んでいるようだ。
列挙子の最大ID
E.maxId
  実際に使われているIDの最大値+1が返る。

異なる列挙型の列挙子は区別される

列挙子の型(クラス)の実体はEnumeration内のValクラスとなる。[2011-01-09]
異なる列挙型を定義しても、それは変わらない。
しかし異なる列挙型の列挙子の型は区別され、お互いに代入することは出来ない。

scala> object E1 extends Enumeration { val e1 = Value(0) }
defined module E1

scala> object E2 extends Enumeration { val e2 = Value(0) }
defined module E2

scala> var e = E1.e1
e: E1.Value = e1

scala> e = E2.e2
<console>:19: error: type mismatch;
 found   : E2.Value
 required: E1.Value
       e = E2.e2
              ^

scala> E1.e1 == E2.e2
res2: Boolean = false

scala> E1.e1 < E2.e2
<console>:19: error: could not find implicit value for parameter ord: scala.math.Ordering[Enumeration#Value]
       E1.e1 < E2.e2
          ^

Scalaの内部クラス(Enumerationで言えばValクラス)がパス依存なので区別されるらしい。
参考: 花井 志生さん(るいもの戯れ言)のScalaのenum

と、いう事は、キャストしてやれば同じものとして扱える…。

scala> var e: Enumeration#Value = E1.e1
e: Enumeration#Value = e1

scala> e = E2.e2
e: Enumeration#Value = e2

列挙子から列挙型を取得してみる

個別の列挙子からその列挙型を取得して、列挙子一覧を取得する方法を考えてみる。[2011-04-15]

列挙子のクラスの実体であるEnumeration#ValueやEnumeration#Valには、列挙型を取得するメソッドは公開されていない。
しかしouterEnumというprivate valがあるので、リフレクションを使えば無理矢理だが取れそう。

object EnumSample extends Enumeration {
  val E1, E2, E3 = Value
}
scala> EnumSample.E1.getClass.getDeclaredFields.foreach { println }
public static final long scala.Enumeration$Val.serialVersionUID
public final int scala.Enumeration$Val.scala$Enumeration$Val$$i
private final java.lang.String scala.Enumeration$Val.name

scala> EnumSample.E1.getClass.getDeclaredMethods.foreach { println }
public scala.Enumeration scala.Enumeration$Val.scala$Enumeration$Val$$$outer()
public java.lang.String scala.Enumeration$Val.toString()
public java.lang.Object scala.Enumeration$Val.readResolve()
public int scala.Enumeration$Val.id()

scala> EnumSample.E1.getClass.getSuperclass.getDeclaredFields.foreach { println }
public static final long scala.Enumeration$Value.serialVersionUID
private final scala.Enumeration scala.Enumeration$Value.scala$Enumeration$$outerEnum
public final scala.Enumeration scala.Enumeration$Value.$outer

scala> EnumSample.E1.getClass.getSuperclass.getDeclaredMethods.foreach { println }
public boolean scala.Enumeration$Value.$less(java.lang.Object)
public boolean scala.Enumeration$Value.$greater(java.lang.Object)
public boolean scala.Enumeration$Value.$less$eq(java.lang.Object)
public boolean scala.Enumeration$Value.$greater$eq(java.lang.Object)
public scala.Enumeration scala.Enumeration$Value.scala$Enumeration$$outerEnum()
public int scala.Enumeration$Value.mask32()
public long scala.Enumeration$Value.mask64()
public scala.Enumeration scala.Enumeration$Value.scala$Enumeration$Value$$$outer()
public boolean scala.Enumeration$Value.equals(java.lang.Object)
public int scala.Enumeration$Value.hashCode()
public int scala.Enumeration$Value.compareTo(java.lang.Object)
public int scala.Enumeration$Value.compare(java.lang.Object)
public int scala.Enumeration$Value.compare(scala.Enumeration$Value)
public abstract int scala.Enumeration$Value.id()

どうやら実体であるValの親クラスValueのプライベートフィールド「scala$Enumeration$$outerEnum」がそれらしい。
また、このゲッターメソッドがなぜかpublicで存在しているので、これを呼び出せそう。
(outerEnumの可視性はprivate[Enumeration]なので、特定のクラスからは呼び出せる必要がある。その為、コンパイルされた実体としてはpublicにならざるを得ないのだろう)

scala> val e = EnumSample.E1.getClass.getMethod("scala$Enumeration$$outerEnum").invoke(EnumSample.E1).asInstanceOf[Enumeration]
e: Enumeration = EnumSample

scala> e.values
res6: e.ValueSet = EnumSample.ValueSet(E1, E2, E3)

Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま