스칼라에서 열거 클래스 를 확장하는 경우와 케이스 클래스 (또는 케이스 오브젝트) 를 사용하는시기에 대한 모범 사례 지침이 있습니까?
그들은 동일한 이점 중 일부를 제공하는 것 같습니다.
enum
(2020 년 중반) 도 참조하십시오 .
스칼라에서 열거 클래스 를 확장하는 경우와 케이스 클래스 (또는 케이스 오브젝트) 를 사용하는시기에 대한 모범 사례 지침이 있습니까?
그들은 동일한 이점 중 일부를 제공하는 것 같습니다.
enum
(2020 년 중반) 도 참조하십시오 .
답변:
한 가지 큰 차이점은 Enumeration
일부 name
문자열 에서 인스턴스화를 지원 한다는 것입니다 . 예를 들면 다음과 같습니다.
object Currency extends Enumeration {
val GBP = Value("GBP")
val EUR = Value("EUR") //etc.
}
그럼 당신은 할 수 있습니다 :
val ccy = Currency.withName("EUR")
이는 열거 형 (예 : 데이터베이스)을 유지하거나 파일에있는 데이터에서 열거를 작성하려는 경우에 유용합니다. 그러나 일반적으로 열거 형은 스칼라에서 약간 어색하고 어색한 추가 기능을 가지고 있으므로 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
은 다음과 같습니다.
"enumeration"의 모든 인스턴스를 반복 할 수 없습니다 . 이것은 확실히 사실이지만, 실제로 이것이 필요한 경우는 거의 없습니다.
지속 가치에서 쉽게 인스턴스화 할 수 없습니다 . 이것은 사실이지만, 모든 열거와 같은 거대한 열거의 경우를 제외하고는 큰 오버 헤드가 아닙니다.
trade.ccy
봉인 된 특성 예시에서 일치하는 항목의 유형을 이해하지 못합니다 .
case
object
s보다 더 큰 (~ 4 배) 코드 풋 프린트를 생성 하지 않습니다 Enumeration
. scala.js
작은 설치 공간이 필요한 프로젝트에 특히 유용합니다 .
업데이트 : 아래에 설명 된 솔루션보다 훨씬 우수한 새로운 매크로 기반 솔루션 이 만들어졌습니다. 이 새로운 매크로 기반 솔루션을 사용하는 것이 좋습니다 . 그리고 Dotty에 대한 계획은 이러한 스타일의 열거 형 솔루션을 언어의 일부로 만들 것입니다. 우와 후!
요약 : Scala 프로젝트 내
에서 Java를 재생산하기위한 세 가지 기본 패턴이 있습니다 Enum
. 세 가지 패턴 중 두 가지; 직접 자바를 사용 Enum
하고 scala.Enumeration
, 스칼라의 철저한 패턴 매칭을 가능하게 할 수 없습니다. 그리고 세번째; "밀봉 된 특성 + 케이스 오브젝트"는 수행하지만 JVM 클래스 / 개체 초기화 복잡성이있어 순서가 일치하지 않는 서수 인덱스 생성이 발생합니다.
두 가지 클래스로 솔루션을 만들었습니다. 이 요지 에있는 Enumeration 및 EnumerationDecorated 입니다. 열거 형 파일이 상당히 커서 (이 라인에 400 줄-구현 컨텍스트를 설명하는 많은 주석이 들어 있으므로)이 스레드에 코드를 게시하지 않았습니다.
세부 사항 :
당신이 묻는 질문은 꽤 일반적입니다. "... 클래스 를 사용하고 확장하는 경우 " 그리고 당신이 가지고있는 특정 프로젝트 요구 사항의 미묘함에 따라 각 대답이 가능한 많은 답변이 있음이 밝혀졌습니다. 답은 세 가지 기본 패턴으로 줄일 수 있습니다.
case
objects
[scala.]Enumeration
시작하려면 열거가 무엇인지에 대한 동일한 기본 아이디어로 작업하고 있는지 확인하십시오. Enum
Java 5 (1.5) 현재 제공되는 관점에서 열거를 정의합시다 .
Enum
열거에 대해 Scala의 패턴 일치 철저 검사를 명시 적으로 활용할 수 있다면 좋을 것입니다. : 다음은 게시 된 가장 일반적인 세 가지 솔루션 패턴의 버전 아래 삶은에서의 모습을 보자
A)를 실제로 직접 사용하여 자바Enum
) 혼합 스칼라 / 자바 프로젝트에서 패턴 (:
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은 열거 형 멤버를 추가 / 제거하거나 기존 열거 형 멤버를 처리하기 위해 새로운 코드를 작성하는 경우 컴파일 시간 문제를 포착하는 데 중요합니다.
따라서 열거에 대한 위의 정의가 주어지면 위의 세 가지 솔루션 중 어느 것도 위의 열거 정의에 요약 된 모든 것을 제공하지 않으므로 작동하지 않습니다.
이러한 각 솔루션은 결국 재 작업 / 확장 / 리팩터링되어 누락 된 요구 사항 중 일부를 처리 할 수 있습니다. 그러나 Java Enum
또는 scala.Enumeration
솔루션을 충분히 확장하여 항목 7을 제공 할 수는 없습니다. 또한 내 프로젝트의 경우 Scala에서 닫힌 유형을 사용하는 데있어 가장 강력한 가치 중 하나입니다. 컴파일 타임 경고 / 오류를 선호하여 프로덕션 런타임 예외 / 실패에서 코드를 수집하지 않고 코드에 간격 / 문제가 있음을 나타냅니다.
이와 관련하여 case object
위의 열거 정의를 모두 포함하는 솔루션을 생성 할 수 있는지 확인하기 위해 경로를 사용하여 작업을 시작했습니다 . 첫 번째 과제는 JVM 클래스 / 객체 초기화 문제의 핵심 ( 이 StackOverflow post 에서 자세히 다룰 것)을 추진하는 것이 었 습니다 . 그리고 마침내 해결책을 찾을 수있었습니다.
내 솔루션은 두 가지 특성입니다. Enumeration 및 EnumerationDecorated 이며 Enumeration
특성이 + 400 줄 이상 (컨텍스트를 설명하는 많은 주석)이 넘기 때문에이 스레드에 붙여 넣기를 잊어 버렸습니다 (페이지를 상당히 축소시킵니다). 자세한 내용은 요점으로 바로 이동하십시오 .
위와 동일한 데이터 아이디어를 사용하여 솔루션을 완성한 것처럼 보이는 부분은 다음과 같습니다 (여기에서 사용할 수있는 주석이 달린 버전 ) 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에있는 ) 새로운 열거 특성 쌍의 사용 예 입니다.
표현 된 한 가지 우려는 열거 멤버 이름을 반복해야한다는 것입니다 ( decorationOrderedSet
위 예에서). 한 번의 반복으로 최소화했지만 두 가지 문제로 인해 더 적게 만드는 방법을 알 수 없었습니다.
getClass.getDeclaredClasses
는 정의되지 않은 순서를 갖습니다 (그리고 case object
소스 코드 의 선언 과 같은 순서는 아닐 것입니다 )이 두 가지 문제를 감안할 때, 나는 암묵적 순서를 생성하려고 포기하고 클라이언트가 명시 적으로 일종의 순서 집합 개념으로 그것을 정의하고 선언해야했습니다. 스칼라 컬렉션에는 삽입 순서 집합 구현이 없으므로 내가 할 수있는 최선의 방법은 List
런타임 집합이 실제로 집합인지 확인하는 것입니다. 내가 이것을 달성하는 것을 선호했던 방법이 아닙니다.
그리고 디자인에이 두 번째 목록 / 세트 순서가 필요하면 위 val
의 ChessPiecesEnhancedDecorated
예에서 추가 case object PAWN2 extends Member
하고 추가하는 Decoration(PAWN2,'P2', 2)
것을 잊을 수 decorationOrderedSet
있었습니다. 따라서 목록이 세트 일뿐만 아니라을 확장하는 모든 케이스 오브젝트를 포함하는지 확인하기위한 런타임 검사가 있습니다 sealed trait Member
. 그것은 특별한 형태의 반사 / 매크로 지옥이었습니다. 요점
에 대한 의견이나 피드백을 남겨주십시오 .
org.scalaolio.util.Enumeration
및 org.scalaolio.util.EnumerationDecorated
: scalaolio.org
케이스 오브젝트는 이미 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)
}
물건은 게으르다. 대신 val을 사용하면리스트를 삭제할 수 있지만 이름을 반복해야합니다.
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") {}
}
부정 행위가 마음에 들지 않으면 reflection API 또는 Google Reflections와 같은 것을 사용하여 열거 값을 미리로드 할 수 있습니다. 지연이없는 대소 문자 객체는 가장 깨끗한 구문을 제공합니다.
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 열거의 모든 장점을 갖춘 훌륭하고 깔끔합니다. 개인적으로 관용 스칼라 코드와 더 잘 일치하도록 객체 외부의 열거 값을 정의합니다.
object Currency extends Enum[Currency]
sealed trait Currency extends Currency.Value
case object EUR extends Currency
case object GBP extends Currency
Currency.values
이전에 액세스 한 값만 가져옵니다. 그 주위에 어떤 방법이 있습니까?
열거에 비해 케이스 클래스를 사용하면 다음과 같은 장점이 있습니다.
케이스 클래스 대신 열거를 사용하면 다음과 같은 장점이 있습니다.
따라서 일반적으로 이름별로 간단한 상수 목록이 필요하면 열거 형을 사용하십시오. 그렇지 않으면 좀 더 복잡한 것이 필요하거나 모든 일치 항목이 지정되어 있는지 알려주는 컴파일러의 안전성을 원한다면 케이스 클래스를 사용하십시오.
업데이트 : 아래 코드에는 여기 에 설명 된 버그가 있습니다 . 아래 테스트 프로그램은 작동하지만 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()
}
각 enum 값 (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
"day match"를 이러한 경고를 원하지 않는 "(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
List와 Maps는 변경할 수 없으므로 열거 자체를 손상시키지 않고 요소를 쉽게 제거하여 하위 집합을 만들 수 있습니다.
다음은 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 세계의 경계선 필멸의 죄"입니다. 저는 의견이 보편적으로 받아 들여질 것이라고 생각하지 않습니다.
여기에 자신의 값 목록을 유지하지 않고도 봉인 된 특성 / 클래스를 열거 형 값으로 사용할 수있는 멋진 간단한 라이브러리가 있습니다. 버그에 의존하지 않는 간단한 매크로에 의존합니다 knownDirectSubclasses
.
2017 년 3 월 업데이트 : Anthony Accioly의 따라 scala.Enumeration/enum
PR은 마감되었습니다.
Dotty (스칼라의 차세대 컴파일러)는 1970 년의 dotty 문제를 주도 할 것입니다. 와 Martin Odersky의 PR 1958 입니다.
참고 : 현재 (6 년 이상 지난 2016 년 8 월) 제거 제안이 있습니다. scala.Enumeration
: 5352 PR
더 이상 사용되지 않음
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의 장점.
- 실제로 작동
- 자바 interop
- 삭제 문제 없음
- 열거를 정의 할 때 혼동되는 미니 DSL이 없음
단점 : 없음
이것은 Scala-JVM
Scala.js
및 Scala-Native ( Scala-JVM에서 지원되지 않는 Java 소스 코드Scala.js/Scala-Native
, Scala-JVM의 기존 API에서 허용되는 열거 형을 정의 할 수 없음)를 지원하는 하나의 코드베이스를 가질 수없는 문제를 해결합니다 .
모든 인스턴스에서 반복하거나 필터링해야 할 경우 케이스 클래스와 열거의 또 다른 단점. 이것은 Enumeration (및 Java enum)의 기본 제공 기능이지만 케이스 클래스는 이러한 기능을 자동으로 지원하지 않습니다.
즉, "케이스 클래스를 사용하여 열거 된 값의 전체 세트 목록을 얻는 쉬운 방법은 없습니다".
사례 클래스를 열거 형으로 만드는 다양한 버전을 보았습니다. 내 버전은 다음과 같습니다.
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)
}
어쩌면 누군가가 내가 한 것처럼 단순히 각 사례 클래스를 목록에 추가하는 것보다 더 나은 트릭을 만들 수 있습니다. 이것은 내가 당시에 생각해 낼 수있는 전부였습니다.
나는 마지막 두 번이 필요했던 두 가지 옵션을왔다 갔다했습니다. 최근까지 밀봉 된 특성 / 케이스 개체 옵션을 선호했습니다.
1) 스칼라 열거 선언
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
이것들 중 어느 것도 실제로 자바 열거 형이 제공하는 모든 것을 충족 시키지는 않지만, 장단점은 다음과 같습니다.
스칼라 열거
장점 :-옵션을 사용하여 인스턴스화하거나 정확한 것으로 가정하는 기능 (영구 저장소에서로드 할 때 더 쉬움)-가능한 모든 값에 대한 반복이 지원됩니다
단점 : 비 포괄적 검색에 대한 컴파일 경고가 지원되지 않습니다 (패턴 일치가 이상적이지 않음)
사례 개체 / 밀봉 된 특성
장점 :-밀봉 된 특성을 사용하여 일부 값을 미리 인스턴스화 할 수 있으며 생성시 다른 값을 주입 할 수 있습니다.-패턴 일치를 완벽하게 지원
단점 :-영구 저장소에서 인스턴스화-종종 여기에서 패턴 일치를 사용하거나 가능한 모든 'enum value'목록을 정의해야합니다.
궁극적으로 내 의견을 바꾸게 한 것은 다음과 같은 내용이었습니다.
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
오버 오버 의 가장 큰 장점은 타입 클래스 패턴 일명 ad-hoc polymorphysm을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() = ...
}