Javaには列挙型のクラスを作成するenum
があり、実際にはコンパイル時に色々自動生成しているらしい。
Scalaにはそれとは別のEnumeration
クラスがあり(むしろ enum
はキーワードとして予約されていない)、これを継承したシングルトンオブジェクトが列挙するインスタンスのグループを表す。
以下サンプル。
Signal.scala
/**
* Define Signal Enumeration
*/
object Signal extends Enumeration {
val Green = Value //=> Val(0, "Green")
val Yellow = Value(2) //=> Val(2, "Yellow")
val Red = Value("RED") //=> Val(3, "RED")
// Alias for Java
def valueOf(name: String) = withName(name)
// Java-like methods addition (can't use implicitly from Java)
implicit class JavaLikeValue(v: Value) {
def name = v.toString
def ordinal = v.id
}
}
使用するときは以下のようにする。
implicit class
があると、いかにも列挙型の値にメソッドを追加したように見える。
ScalaMain.scala
/**
* Use Signal
*/
object ScalaMain extends App {
// Iteration
Signal.values.foreach { signal =>
println(s"id: ${signal.id}, toString: ${signal}")
}
// Get singleton instance
val g = Signal.Green
val y = Signal(2) // by id
val r = Signal.withName("RED") // by name
// Mapping function
def toMessage = {
import Signal._ // import for shortcut
// pattern match (like switch-case in Java)
(_: Value) match {
case Green => "Go ahead"
case Yellow => "Be careful"
case Red => "Stop"
}
}
println( toMessage(r) )
// implicit conversion
println( g.name )
println( y.ordinal )
}
一方これをJavaから参照して使う場合は以下のようになる。
Main.java
public class Main {
public static void main(String[] args) {
// Signal class
System.out.println(Signal.class.toString());
// Signal$ class (Signal.type @ Scala)
System.out.println(Signal$.class.toString());
// Signal singleton object
Signal$ obj = Signal$.MODULE$;
System.out.println(obj.toString());
// Get enumeration instance
System.out.println(Signal.Green().toString());
System.out.println(Signal.apply(2).toString());
System.out.println(Signal.withName("RED").toString());
System.out.println(Signal.valueOf("RED").toString());
// Instance Type (Inner class of singleton object class)
Signal$.Value a = Signal.Yellow();
System.out.println(a.id());
// Use methods explicitly
System.out.println(new Signal.JavaLikeValue(Signal.Green()).name());
}
}
JavaからScalaコードを参照する場合は、以下に気をつける。
-
object Signal
の型は、Signal$
として参照できる。 - 列挙子(インスタンス)の型は
Signal$.Value
(内部クラス)となる。 - 暗黙の変換(implicit conversion)は明示的に呼び出す。(Scalaコンパイラの実行時に処理するため)
- Javaのswitch-case文では利用できない(?)
ちなみに、Scalaでは type Signal = Signal.Value
とすることで型エイリアスを宣言できるが、これもコンパイラが解決してくれるだけなので、出来上がったクラスを javap でチェックしても、型エイリアスの情報は消えている。