回答:
1つの大きな違いは、Enumeration
一部のname
文字列からインスタンス化するためのサポートが付属していることです。例えば:
object Currency extends Enumeration {
val GBP = Value("GBP")
val EUR = Value("EUR") //etc.
}
次に、次のことができます。
val ccy = Currency.withName("EUR")
これは、列挙を永続化する(データベースなど)か、ファイルにあるデータから作成する場合に役立ちます。ただし、一般的に、列挙型はScalaで少し扱いにくく、扱いにくいアドオンのように感じるため、今ではcase object
s を使用する傾向があります。A case object
は列挙型よりも柔軟です。
sealed trait Currency { def name: String }
case object EUR extends Currency { val name = "EUR" } //etc.
case class UnknownCurrency(name: String) extends Currency
だから今私は利点があります...
trade.ccy match {
case EUR =>
case UnknownCurrency(code) =>
}
通りの@ chaotic3quilibriumは(読んで容易にするためのいくつかの修正で)指摘しました:
"UnknownCurrency(code)"パターンに関しては、
Currency
型の閉じたセットの性質を "壊す"よりも、通貨コード文字列を見つけられないように処理する他の方法があります。UnknownCurrency
タイプであるCurrency
ことは、APIの他の部分に忍び込むことができます。そのケースを外に押し出して
Enumeration
、クライアントにOption[Currency]
実際に一致する問題があることを明確に示すタイプを処理させ、APIのユーザーにそれを整理するように「勧める」ことをお勧めします。
ここで他の回答を補足するために、case object
sに対するEnumeration
sの主な欠点は次のとおりです。
「列挙」のすべてのインスタンスを反復することはできません。これは確かに事実ですが、これが必要になることは実際には非常にまれです。
永続化された値から簡単にインスタンス化することはできません。これも当てはまりますが、巨大な列挙(たとえば、すべての通貨)の場合を除いて、これは大きなオーバーヘッドにはなりません。
trade.ccy
封印された特性の例で一致するアイテムのタイプがわかりません。
case
object
より大きな(〜4x)コードフットプリントを生成しないのEnumeration
ですか?特にscala.js
小さなフットプリントを必要とするプロジェクトのための有用な区別。
更新:以下に概説するソリューションよりもはるかに優れ た新しいマクロベースのソリューションが作成されました。この新しいマクロベースのソリューションを使用することを強くお勧めします。また、Dottyの計画では、このスタイルの列挙型ソリューションを言語の一部にする予定です。うわー!
概要: Scalaプロジェクト内で
Javaを再現しようとする場合、3つの基本的なパターンがありますEnum
。3つのパターンのうちの2つ。Java Enum
とを直接使用してscala.Enumeration
、Scalaの完全なパターンマッチングを有効にすることはできません。そして3つ目。「封印された特性+ケースオブジェクト」にはありますが、JVMクラス/オブジェクトの初期化が複雑ですなり、順序インデックスの生成に一貫性がなくなります。
2つのクラスを持つソリューションを作成しました。このGistにあるEnumerationおよびEnumerationDecorated。Enumerationのファイルが非常に大きい(+400行-実装コンテキストを説明する多くのコメントが含まれている)ため、このスレッドにコードを投稿しませんでした。
詳細:
あなたが尋ねている質問はかなり一般的です。"... クラスを使用する場合と拡張する場合
case
objects
[scala.]Enumeration
」そして、多くの可能な答えがあり、それぞれの答えはあなたが持っている特定のプロジェクト要件の微妙さによって異なります。答えは、3つの基本パターンに減らすことができます。
最初に、列挙とは何かという同じ基本的な考え方から作業していることを確認しましょう。Enum
Java 5(1.5)の時点で提供されているものに関して主に列挙を定義してみましょう:
Enum
と、列挙のためにScalaのパターンマッチングの網羅性チェックを明示的に活用できるといいでしょう 次に、投稿された3つの最も一般的なソリューションパターンの煮詰めたバージョンを見てみましょう
。A)実際にJavaEnum
パターンを直接使用します(ScalaとJavaの混合プロジェクトで)。
public enum ChessPiece {
KING('K', 0)
, QUEEN('Q', 9)
, BISHOP('B', 3)
, KNIGHT('N', 3)
, ROOK('R', 5)
, PAWN('P', 1)
;
private char character;
private int pointValue;
private ChessPiece(char character, int pointValue) {
this.character = character;
this.pointValue = pointValue;
}
public int getCharacter() {
return character;
}
public int getPointValue() {
return pointValue;
}
}
列挙定義の次のアイテムは使用できません。
私の現在のプロジェクトでは、ScalaとJavaの混合プロジェクトパスウェイのリスクを取る利点がありません。また、混合プロジェクトを実行することを選択できたとしても、列挙型メンバーを追加/削除したり、既存の列挙型メンバーを処理するための新しいコードを書いたりする場合、コンパイル時の問題をキャッチするには、項目7が重要です。
B)「sealed trait
+case objects
」パターンの使用:
sealed trait ChessPiece {def character: Char; def pointValue: Int}
object ChessPiece {
case object KING extends ChessPiece {val character = 'K'; val pointValue = 0}
case object QUEEN extends ChessPiece {val character = 'Q'; val pointValue = 9}
case object BISHOP extends ChessPiece {val character = 'B'; val pointValue = 3}
case object KNIGHT extends ChessPiece {val character = 'N'; val pointValue = 3}
case object ROOK extends ChessPiece {val character = 'R'; val pointValue = 5}
case object PAWN extends ChessPiece {val character = 'P'; val pointValue = 1}
}
列挙定義の次のアイテムは使用できません。
列挙型定義の項目5と6を実際に満たしていることは間違いありません。5の場合、効率的であると主張するのは簡単です。6の場合、追加の関連する単一性データを保持するように拡張するのは、実際には簡単ではありません。
C)scala.Enumeration
パターンの使用(このStackOverflow回答に触発された):
object ChessPiece extends Enumeration {
val KING = ChessPieceVal('K', 0)
val QUEEN = ChessPieceVal('Q', 9)
val BISHOP = ChessPieceVal('B', 3)
val KNIGHT = ChessPieceVal('N', 3)
val ROOK = ChessPieceVal('R', 5)
val PAWN = ChessPieceVal('P', 1)
protected case class ChessPieceVal(character: Char, pointValue: Int) extends super.Val()
implicit def convert(value: Value) = value.asInstanceOf[ChessPieceVal]
}
列挙定義の次の項目は使用できません(Java Enumを直接使用するためのリストと同じになる可能性があります)。
繰り返しになりますが、現在のプロジェクトでは、列挙型メンバーを追加/削除したり、既存の列挙型メンバーを処理するための新しいコードを書いたりしている場合に、コンパイル時の問題をキャッチできるようにするために、アイテム7は重要です。
したがって、上記の列挙の定義が与えられた場合、上記の列挙の定義で概説されているすべてを提供するわけではないため、上記の3つのソリューションはどれも機能しません。
これらの各ソリューションは、最終的には、それぞれの不足している要件の一部をカバーするように再加工/拡張/リファクタリングできます。ただし、Java Enum
もscala.Enumeration
ソリューションも十分に拡張して項目7を提供することはできません。そして、私自身のプロジェクトでは、これはScala内でクローズド型を使用することの最も説得力のある値の1つです。コンパイル時の警告/エラーは、コードにギャップ/問題があることを示すために、運用時の例外/エラーから収集する必要があるのではなく、強く推奨します。
その点で、私はcase object
経路を使用して、上記の列挙の定義のすべてをカバーするソリューションを作成できるかどうかを確認することに取り掛かりました。最初の課題は、JVMクラス/オブジェクト初期化問題の核心を突き抜けることでした(このStackOverflowの投稿で詳細に説明されています)。そして、ようやく解決策を見つけることができました。
私の解決策は2つの特徴なので、EnumerationとEnumerationDecorated、そしてEnumeration
トレイトは+400行を超える(コンテキストを説明するたくさんのコメント)ので、このスレッドに貼り付けるのを忘れています(ページをかなり伸ばしてしまいます)。詳細については、Gistに直接ジャンプしてください。
上記と同じデータのアイデアを使用してソリューションが最終的にどのように見えるかを以下に示します(完全にコメントされたバージョンはここにあります)EnumerationDecorated
。
import scala.reflect.runtime.universe.{TypeTag,typeTag}
import org.public_domain.scala.utils.EnumerationDecorated
object ChessPiecesEnhancedDecorated extends EnumerationDecorated {
case object KING extends Member
case object QUEEN extends Member
case object BISHOP extends Member
case object KNIGHT extends Member
case object ROOK extends Member
case object PAWN extends Member
val decorationOrderedSet: List[Decoration] =
List(
Decoration(KING, 'K', 0)
, Decoration(QUEEN, 'Q', 9)
, Decoration(BISHOP, 'B', 3)
, Decoration(KNIGHT, 'N', 3)
, Decoration(ROOK, 'R', 5)
, Decoration(PAWN, 'P', 1)
)
final case class Decoration private[ChessPiecesEnhancedDecorated] (member: Member, char: Char, pointValue: Int) extends DecorationBase {
val description: String = member.name.toLowerCase.capitalize
}
override def typeTagMember: TypeTag[_] = typeTag[Member]
sealed trait Member extends MemberDecorated
}
これは、列挙型定義で必要とされ、概説されているすべての機能を実装するために作成した(このGistにある)列挙型トレイトの新しいペアの使用例です。
表明された懸念の1つは、列挙メンバー名を繰り返す必要があることです(decorationOrderedSet
上の例では)。私はそれを1回の繰り返しに最小化しましたが、2つの問題のため、それをさらに少なくする方法を理解できませんでした。
getClass.getDeclaredClasses
順序は未定義です(case object
ソースコードの宣言と同じ順序になることはほとんどありません)。これらの2つの問題を考えると、暗黙の順序付けを生成することをあきらめ、クライアントに明示的に要求し、何らかの順序付けされたセットの概念でそれを宣言する必要がありました。Scalaコレクションには挿入順のセット実装がないため、私ができる最善の方法は、a List
を使用してから、実行時にそれが本当にセットであることを確認することでした。私がこれを達成したいと思った方法ではありません。
また、設計でこの2番目のリスト/セットの順序付けが必要であることval
を考えると、ChessPiecesEnhancedDecorated
上記の例では、case object PAWN2 extends Member
を追加Decoration(PAWN2,'P2', 2)
してからに追加するのを忘れることがありましたdecorationOrderedSet
。したがって、リストがセットであるだけでなく、を拡張するすべてのケースオブジェクトが含まれていることを確認するランタイムチェックがありますsealed trait Member
。それは特別な形の反射/マクロ地獄でした。Gistに
コメントやフィードバックを残してください。
org.scalaolio.util.Enumeration
とorg.scalaolio.util.EnumerationDecorated
:scalaolio.org
Caseオブジェクトは既にtoStringメソッドの名前を返しているため、個別に渡す必要はありません。これはjhoに似たバージョンです(簡潔にするために、便利なメソッドは省略されています)。
trait Enum[A] {
trait Value { self: A => }
val values: List[A]
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
val values = List(EUR, GBP)
}
オブジェクトは遅延します。代わりにvalsを使用すると、リストを削除できますが、名前を繰り返す必要があります。
trait Enum[A <: {def name: String}] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed abstract class Currency(val name: String) extends Currency.Value
object Currency extends Enum[Currency] {
val EUR = new Currency("EUR") {}
val GBP = new Currency("GBP") {}
}
不正行為が気にならない場合は、リフレクションAPIまたはGoogleリフレクションなどを使用して、列挙値をプリロードできます。レイジーでないケースオブジェクトは、最も簡潔な構文を提供します。
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
ケースクラスとJava列挙のすべての利点を備えた、すっきりとしたクリーン。個人的には、オブジェクトの外で列挙値を定義して、慣用的なScalaコードとよりよく一致させます。
object Currency extends Enum[Currency]
sealed trait Currency extends Currency.Value
case object EUR extends Currency
case object GBP extends Currency
Currency.values
、以前にアクセスした値のみが返されます。それを回避する方法はありますか?
列挙よりもケースクラスを使用する利点は次のとおりです。
ケースクラスの代わりに列挙型を使用する利点は次のとおりです。
したがって、一般に、名前による単純な定数のリストが必要なだけの場合は、列挙を使用します。それ以外の場合で、もう少し複雑なものが必要な場合、またはすべての一致が指定されているかどうかをコンパイラーに通知してさらに安全にしたい場合は、ケースクラスを使用します。
更新:以下のコードには、ここで説明するバグがあります。以下のテストプログラムは機能しますが、DayOfWeek.Monの前に(たとえば)DayOfWeek.Monを使用すると、DayOfWeekが初期化されていないために失敗します(内部オブジェクトを使用しても外部オブジェクトは初期化されません)。val enums = Seq( DayOfWeek )
メインクラスで何かを行い、列挙型を強制的に初期化する場合、またはchaotic3quilibriumの変更を使用する場合は、このコードを引き続き使用できます。マクロベースの列挙型を楽しみにしています!
お望みならば
次に、以下が興味深いかもしれません。フィードバックを歓迎します。
この実装には、抽象EnumおよびEnumVal基本クラスがあり、これらを拡張します。これらのクラスは1分後に表示されますが、最初に、列挙型を定義する方法を次に示します。
object DayOfWeek extends Enum {
sealed abstract class Val extends EnumVal
case object Mon extends Val; Mon()
case object Tue extends Val; Tue()
case object Wed extends Val; Wed()
case object Thu extends Val; Thu()
case object Fri extends Val; Fri()
case object Sat extends Val; Sat()
case object Sun extends Val; Sun()
}
有効にするためには、各列挙値(applyメソッドを呼び出す)を使用する必要があることに注意してください。[私は、具体的に要求しない限り、内部オブジェクトが怠惰にならないことを望みます。おもう。]
もちろん、必要に応じて、メソッド/データをDayOfWeek、Val、または個々のケースオブジェクトに追加することもできます。
そして、このような列挙型の使用方法は次のとおりです。
object DayOfWeekTest extends App {
// To get a map from Int id to enum:
println( DayOfWeek.valuesById )
// To get a map from String name to enum:
println( DayOfWeek.valuesByName )
// To iterate through a list of the enum values in definition order,
// which can be made different from ID order, and get their IDs and names:
DayOfWeek.values foreach { v => println( v.id + " = " + v ) }
// To sort by ID or name:
println( DayOfWeek.values.sorted mkString ", " )
println( DayOfWeek.values.sortBy(_.toString) mkString ", " )
// To look up enum values by name:
println( DayOfWeek("Tue") ) // Some[DayOfWeek.Val]
println( DayOfWeek("Xyz") ) // None
// To look up enum values by id:
println( DayOfWeek(3) ) // Some[DayOfWeek.Val]
println( DayOfWeek(9) ) // None
import DayOfWeek._
// To compare enums as ordinals:
println( Tue < Fri )
// Warnings about non-exhaustive pattern matches:
def aufDeutsch( day: DayOfWeek.Val ) = day match {
case Mon => "Montag"
case Tue => "Dienstag"
case Wed => "Mittwoch"
case Thu => "Donnerstag"
case Fri => "Freitag"
// Commenting these out causes compiler warning: "match is not exhaustive!"
// case Sat => "Samstag"
// case Sun => "Sonntag"
}
}
コンパイルすると次のようになります。
DayOfWeekTest.scala:31: warning: match is not exhaustive!
missing combination Sat
missing combination Sun
def aufDeutsch( day: DayOfWeek.Val ) = day match {
^
one warning found
このような警告が不要な場合は、「日一致」を「(日:@unchecked)一致」に置き換えるか、単に末尾にすべてを大文字にすることを含めることができます。
上記のプログラムを実行すると、次の出力が得られます。
Map(0 -> Mon, 5 -> Sat, 1 -> Tue, 6 -> Sun, 2 -> Wed, 3 -> Thu, 4 -> Fri)
Map(Thu -> Thu, Sat -> Sat, Tue -> Tue, Sun -> Sun, Mon -> Mon, Wed -> Wed, Fri -> Fri)
0 = Mon
1 = Tue
2 = Wed
3 = Thu
4 = Fri
5 = Sat
6 = Sun
Mon, Tue, Wed, Thu, Fri, Sat, Sun
Fri, Mon, Sat, Sun, Thu, Tue, Wed
Some(Tue)
None
Some(Thu)
None
true
リストとマップは不変であるため、列挙型自体を壊すことなく、要素を簡単に削除してサブセットを作成できることに注意してください。
Enumクラス自体(およびその中のEnumVal)は次のとおりです。
abstract class Enum {
type Val <: EnumVal
protected var nextId: Int = 0
private var values_ = List[Val]()
private var valuesById_ = Map[Int ,Val]()
private var valuesByName_ = Map[String,Val]()
def values = values_
def valuesById = valuesById_
def valuesByName = valuesByName_
def apply( id : Int ) = valuesById .get(id ) // Some|None
def apply( name: String ) = valuesByName.get(name) // Some|None
// Base class for enum values; it registers the value with the Enum.
protected abstract class EnumVal extends Ordered[Val] {
val theVal = this.asInstanceOf[Val] // only extend EnumVal to Val
val id = nextId
def bumpId { nextId += 1 }
def compare( that:Val ) = this.id - that.id
def apply() {
if ( valuesById_.get(id) != None )
throw new Exception( "cannot init " + this + " enum value twice" )
bumpId
values_ ++= List(theVal)
valuesById_ += ( id -> theVal )
valuesByName_ += ( toString -> theVal )
}
}
}
そして、IDを制御し、データ/メソッドをVal抽象化および列挙自体に追加する、より高度な使用方法を次に示します。
object DayOfWeek extends Enum {
sealed abstract class Val( val isWeekday:Boolean = true ) extends EnumVal {
def isWeekend = !isWeekday
val abbrev = toString take 3
}
case object Monday extends Val; Monday()
case object Tuesday extends Val; Tuesday()
case object Wednesday extends Val; Wednesday()
case object Thursday extends Val; Thursday()
case object Friday extends Val; Friday()
nextId = -2
case object Saturday extends Val(false); Saturday()
case object Sunday extends Val(false); Sunday()
val (weekDays,weekendDays) = values partition (_.isWeekday)
}
var
することはFPの世界での境界的な大罪である」—私は意見が広く受け入れられているとは思いません。
ここには、独自の値のリストを維持する必要なしに、enum値として封印された特性/クラスを使用できる、素晴らしいシンプルなlibがあります。バギーに依存しない単純なマクロに依存していknownDirectSubclasses
ます。
2017年3月更新:Anthony Acciolyのコメントにより、scala.Enumeration/enum
PRは終了しました。
Dotty(Scalaの次世代コンパイラ)が主導しますが、 1970年のDottyの問題と Martin OderskyのPR 1958が主流です。
注:現在(2016年8月、6年以上後)に削除の提案がありscala.Enumeration
ます:PR 5352
非推奨
scala.Enumeration
、@enum
注釈を追加構文
@enum
class Toggle {
ON
OFF
}
可能な実装例です。意図は、特定の制限(ネスト、再帰、またはコンストラクターパラメーターの変更なし)に準拠するADTもサポートすることです。次に例を示します。
@enum
sealed trait Toggle
case object ON extends Toggle
case object OFF extends Toggle
軽減されていない災害であるを非推奨にし
scala.Enumeration
ます。@enumのscala.Enumerationに対する利点:
- 実際に動作します
- Java相互運用
- 消去の問題はありません
- 列挙型を定義するときに理解するのに混乱しないmini-DSL
短所:なし。
これは、Scala-JVM
Scala.js
とScala-Native をサポートする1つのコードベースを使用できない問題に対処します(JavaソースコードはでサポートされていませんScala.js/Scala-Native
。Scalaソースコードは、Scala-JVMの既存のAPIで受け入れられる列挙型を定義できません)。
すべてのインスタンスを反復またはフィルタリングする必要がある場合の、列挙型とケースクラスのもう1つの欠点。これは列挙型(およびJava列挙型)の組み込み機能ですが、ケースクラスはこの機能を自動的にサポートしません。
言い換えると、「ケースクラスで列挙値の合計セットのリストを取得する簡単な方法はありません」。
列挙型を模倣するケースクラスを作成するさまざまなバージョンを見てきました。これが私のバージョンです:
trait CaseEnumValue {
def name:String
}
trait CaseEnum {
type V <: CaseEnumValue
def values:List[V]
def unapply(name:String):Option[String] = {
if (values.exists(_.name == name)) Some(name) else None
}
def unapply(value:V):String = {
return value.name
}
def apply(name:String):Option[V] = {
values.find(_.name == name)
}
}
これにより、次のようなケースクラスを構築できます。
abstract class Currency(override name:String) extends CaseEnumValue {
}
object Currency extends CaseEnum {
type V = Site
case object EUR extends Currency("EUR")
case object GBP extends Currency("GBP")
var values = List(EUR, GBP)
}
多分誰かが私がしたようにリストに各ケースクラスを単に追加するより良いトリックを思いつくかもしれません。当時はこれで思いつくことができました。
私はこれらの2つのオプションを必要とする最後の数回往復してきました。最近まで、私の好みは封印された特性/ケースオブジェクトオプションでした。
1)Scala列挙宣言
object OutboundMarketMakerEntryPointType extends Enumeration {
type OutboundMarketMakerEntryPointType = Value
val Alpha, Beta = Value
}
2)封印された特性+ケースオブジェクト
sealed trait OutboundMarketMakerEntryPointType
case object AlphaEntryPoint extends OutboundMarketMakerEntryPointType
case object BetaEntryPoint extends OutboundMarketMakerEntryPointType
これらはいずれも、Java列挙が提供するすべてのものを実際には満たしていませんが、以下の長所と短所があります。
Scala列挙
長所:-オプションでインスタンス化するための関数、または正確に直接仮定するための関数(永続ストアからロードする場合により簡単)-可能なすべての値に対する反復がサポートされます
短所:-網羅的ではない検索のコンパイル警告はサポートされていません(パターンマッチングが理想的ではなくなります)
ケースオブジェクト/封印された特性
長所:-密封された特性を使用して、一部の値を事前にインスタンス化し、作成時に他の値を注入できます-パターンマッチングの完全サポート(定義済みの適用/適用解除メソッド)
短所:-永続ストアからのインスタンス化-ここでパターンマッチングを使用するか、考えられるすべての「列挙値」の独自のリストを定義する必要があります
最終的に私の意見を変えたのは、次のスニペットのようなものでした。
object DbInstrumentQueries {
def instrumentExtractor(tableAlias: String = "s")(rs: ResultSet): Instrument = {
val symbol = rs.getString(tableAlias + ".name")
val quoteCurrency = rs.getString(tableAlias + ".quote_currency")
val fixRepresentation = rs.getString(tableAlias + ".fix_representation")
val pointsValue = rs.getInt(tableAlias + ".points_value")
val instrumentType = InstrumentType.fromString(rs.getString(tableAlias +".instrument_type"))
val productType = ProductType.fromString(rs.getString(tableAlias + ".product_type"))
Instrument(symbol, fixRepresentation, quoteCurrency, pointsValue, instrumentType, productType)
}
}
object InstrumentType {
def fromString(instrumentType: String): InstrumentType = Seq(CurrencyPair, Metal, CFD)
.find(_.toString == instrumentType).get
}
object ProductType {
def fromString(productType: String): ProductType = Seq(Commodity, Currency, Index)
.find(_.toString == productType).get
}
.get
呼び出しは恐ろしいだった-次のように代わりに、私は単に列挙にwithNameメソッドを呼び出すことができます列挙を使用しました:
object DbInstrumentQueries {
def instrumentExtractor(tableAlias: String = "s")(rs: ResultSet): Instrument = {
val symbol = rs.getString(tableAlias + ".name")
val quoteCurrency = rs.getString(tableAlias + ".quote_currency")
val fixRepresentation = rs.getString(tableAlias + ".fix_representation")
val pointsValue = rs.getInt(tableAlias + ".points_value")
val instrumentType = InstrumentType.withNameString(rs.getString(tableAlias + ".instrument_type"))
val productType = ProductType.withName(rs.getString(tableAlias + ".product_type"))
Instrument(symbol, fixRepresentation, quoteCurrency, pointsValue, instrumentType, productType)
}
}
ですから、私の好みは、値がリポジトリからアクセスすることを意図している場合は列挙型を使用し、そうでない場合はケースオブジェクト/密封された特性を使用することだと思います。
私は好みますcase objects
(個人的な好みの問題です)。そのアプローチに固有の問題(文字列を解析してすべての要素を反復処理する)に対処するために、完璧ではないが効果的な数行を追加しました。
ここにコードを貼り付けていますが、それが有用であり、他の人がそれを改善できると期待しています。
/**
* Enum for Genre. It contains the type, objects, elements set and parse method.
*
* This approach supports:
*
* - Pattern matching
* - Parse from name
* - Get all elements
*/
object Genre {
sealed trait Genre
case object MALE extends Genre
case object FEMALE extends Genre
val elements = Set (MALE, FEMALE) // You have to take care this set matches all objects
def apply (code: String) =
if (MALE.toString == code) MALE
else if (FEMALE.toString == code) FEMALE
else throw new IllegalArgumentException
}
/**
* Enum usage (and tests).
*/
object GenreTest extends App {
import Genre._
val m1 = MALE
val m2 = Genre ("MALE")
assert (m1 == m2)
assert (m1.toString == "MALE")
val f1 = FEMALE
val f2 = Genre ("FEMALE")
assert (f1 == f2)
assert (f1.toString == "FEMALE")
try {
Genre (null)
assert (false)
}
catch {
case e: IllegalArgumentException => assert (true)
}
try {
Genre ("male")
assert (false)
}
catch {
case e: IllegalArgumentException => assert (true)
}
Genre.elements.foreach { println }
}
GatesDaの答えを機能させる方法をまだ探している人のために、インスタンス化するように宣言した後、ケースオブジェクトを参照するだけです。
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency;
EUR //THIS IS ONLY CHANGE
case object GBP extends Currency; GBP //Inline looks better
}
私が持っていることの最大の利点だと思うcase classes
以上にenumerations
あなたが使用できるということである型クラスのパターンを別名アドホックpolymorphysm。次のような列挙型を一致させる必要はありません:
someEnum match {
ENUMA => makeThis()
ENUMB => makeThat()
}
代わりに次のようなものが得られます:
def someCode[SomeCaseClass](implicit val maker: Maker[SomeCaseClass]){
maker.make()
}
implicit val makerA = new Maker[CaseClassA]{
def make() = ...
}
implicit val makerB = new Maker[CaseClassB]{
def make() = ...
}