Scalaでケースクラス(またはケースオブジェクト)を使用する場合と列挙を拡張する場合のベストプラクティスのガイドラインはありますか?
それらは同じ利点のいくつかを提供するようです。
Scalaでケースクラス(またはケースオブジェクト)を使用する場合と列挙を拡張する場合のベストプラクティスのガイドラインはありますか?
それらは同じ利点のいくつかを提供するようです。
大きな違いの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を使用する傾向があります。Acase 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の主な欠点は次のとおりです。
「列挙」のすべてのインスタンスを反復処理することはできません。これは確かに事実ですが、これが必要になることは実際には非常にまれであることがわかりました。
永続化された値から簡単にインスタンス化することはできません。これも当てはまりますが、巨大な列挙(たとえば、すべての通貨)の場合を除いて、これは大きなオーバーヘッドを示しません。
更新:以下に概説するソリューションよりもはるかに優れた、 新しいマクロベースのソリューションが作成されました。この新しいマクロベースのソリューションを使用することを強くお勧めします。そして、Dottyの計画により、このスタイルの列挙型ソリューションが言語の一部になるようです。うわぁ!
概要: Scalaプロジェクト内で
Javaを再現しようとするための3つの基本的なパターンがあります。Enum
3つのパターンのうちの2つ。Enum
Javaとを直接使用してscala.Enumeration
、Scalaの徹底的なパターンマッチングを有効にすることはできません。そして3つ目。「封印されたトレイト+ケースオブジェクト」はありますが、JVMクラス/オブジェクトの初期化が複雑になり、順序インデックスの生成に一貫性がなくなります。
2つのクラスでソリューションを作成しました。このGistにあるEnumerationとEnumerationDecorated。列挙用のファイルが非常に大きかったため、このスレッドにコードを投稿しませんでした(+400行-実装コンテキストを説明するコメントがたくさん含まれています)。
詳細:
あなたが尋ねている質問はかなり一般的です。「...クラスを使用する場合と拡張する場合」。そして、多くの可能な答えがあり、それぞれの答えは、あなたが持っている特定のプロジェクト要件の微妙さに依存していることがわかります。答えは3つの基本的なパターンに減らすことができます。
case
objects
[scala.]Enumeration
まず、列挙とは何かという同じ基本的な考え方に基づいて作業していることを確認しましょう。Enum
主にJava5 (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)パターンの使用(このStackOverflowの回答scala.Enumeration
に触発された):
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列挙型を直接使用する場合のリストと同じになる可能性があります)。
繰り返しますが、現在のプロジェクトでは、列挙型メンバーを追加/削除する場合、または既存の列挙型メンバーを処理するための新しいコードを記述している場合に、コンパイル時の問題をキャッチできるようにするために、項目7が重要です。
したがって、上記の列挙型の定義を考えると、上記の3つのソリューションはいずれも、上記の列挙型の定義で概説されているすべてを提供するわけではないため、機能しません。
これらの各ソリューションは、最終的には再加工/拡張/リファクタリングして、それぞれの不足している要件の一部をカバーしようとすることができます。ただし、JavaEnum
も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コレクションには挿入順序セットの実装がないため、私ができる最善の方法は、List
それが本当にセットであるかどうかを実行時にチェックすることでした。それは私がこれを達成することを望んでいた方法ではありません。
そして、この2番目のリスト/セットの順序が必要な設計を考えると、上記val
のChessPiecesEnhancedDecorated
例では、を追加してから追加するのをcase object PAWN2 extends Member
忘れることができました。したがって、リストがセットであるだけでなく、を拡張するすべてのケースオブジェクトが含まれていることを確認するためのランタイムチェックがあります。それは、特別な形の反射/マクロ地獄でした。要旨
にコメントやフィードバックを残してください。Decoration(PAWN2,'P2', 2)
decorationOrderedSet
sealed trait Member
ケースオブジェクトはすでに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") {}
}
不正行為を気にしない場合は、reflectionAPIまたはGoogleReflectionsなどを使用して列挙値をプリロードできます。遅延のないケースオブジェクトは、最もクリーンな構文を提供します。
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
列挙よりもケースクラスを使用する利点は次のとおりです。
ケースクラスの代わりに列挙を使用する利点は次のとおりです。
したがって、一般に、名前ごとの単純な定数のリストが必要な場合は、列挙型を使用します。それ以外の場合、もう少し複雑なものが必要な場合、またはすべての一致が指定されているかどうかをコンパイラーが安全に通知する必要がある場合は、ユースケースクラスを使用します。
更新:以下のコードにはバグがあり、ここで説明されています。以下のテストプログラムは機能しますが、たとえばDayOfWeek自体の前にDayOfWeek.Monを使用すると、DayOfWeekが初期化されていないため失敗します(内部オブジェクトを使用しても外部オブジェクトは初期化されません)。val enums = Seq( DayOfWeek )
メインクラスのように列挙型の初期化を強制する場合、またはchaotic3quilibriumの変更を使用する場合は、このコードを引き続き使用できます。マクロベースの列挙型を楽しみにしています!
お望みならば
次に、以下が興味深いかもしれません。フィードバックを歓迎します。
この実装には、拡張する抽象EnumおよびEnumVal基本クラスがあります。これらのクラスはすぐにわかりますが、最初に、列挙型を定義する方法は次のとおりです。
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
このような警告が不要な場合は、「daymatch」を「(day:@unchecked)match」に置き換えるか、最後にキャッチオールケースを含めることができます。
上記のプログラムを実行すると、次の出力が得られます。
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)
}
ここに、独自の値のリストを維持することなく、列挙型の値として封印された特性/クラスを使用できる、優れた単純なライブラリがあります。これは、バギーに依存しない単純なマクロに依存していknownDirectSubclasses
ます。
2017年3月の更新:Anthony Acciolyのコメントにより、scala.Enumeration/enum
PRは終了しました。
Dotty(Scala用の次世代コンパイラ)が主導権を握りますが、 dottyの問題は1970年、 MartinOderskyの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
。scala.Enumerationに対する@enumの利点:
- 実際に動作します
- Java相互運用
- 消去の問題はありません
- 列挙を定義するときに学ぶための紛らわしいミニDSLはありません
短所:なし。
Scala.js
これは、Scala-JVMとScala-Nativeをサポートする1つのコードベースを持つことができないという問題に対処します(JavaソースコードはサポートされていScala.js/Scala-Native
ません。Scalaソースコードは、Scala-JVM上の既存のAPIで受け入れられる列挙を定義できません)。
すべてのインスタンスにわたって反復またはフィルタリングする必要がある場合の、ケースクラスと列挙のもう1つの欠点。これは列挙型(およびJava列挙型)の組み込み機能ですが、ケースクラスはそのような機能を自動的にサポートしません。
言い換えると、「ケースクラスを使用して列挙値の全セットのリストを取得する簡単な方法はありません」。
他のJVM言語(Javaなど)との相互運用性を維持することを真剣に考えている場合、最良のオプションはJava列挙型を作成することです。scala.Enumeration
これらは、ScalaコードとJavaコードの両方から透過的に機能します。これは、オブジェクトやケースオブジェクトについて言うことができる以上のものです。回避できるのであれば、GitHubの新しいホビープロジェクトごとに新しい列挙型ライブラリを用意しないでください。
ケースクラスを列挙型に模倣するさまざまなバージョンを見てきました。これが私のバージョンです:
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)
}
たぶん、誰かが私がしたように単に各ケースクラスをリストに追加するよりも良いトリックを思い付くかもしれません。当時私が思いついたのはこれだけでした。
私は好きです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 }
}
私はこれらの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)
}
}
したがって、今後の私の好みは、値がリポジトリからアクセスされることを意図している場合は列挙を使用し、それ以外の場合はオブジェクト/封印された特性をケースに入れることだと思います。
GatesDaの回答を機能させる方法をまだ探している人のために:インスタンス化するために宣言した後、caseオブジェクトを参照することができます:
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
)を使用できることだと思います。次のような列挙型に一致する必要はありません。
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() = ...
}