स्काला में केस ऑब्जेक्ट्स एन्यूमरेशन


231

क्या स्कैला में प्रसार संख्या बनाम केस कक्षाओं (या केस ऑब्जेक्ट्स) का उपयोग करने के लिए कोई सर्वोत्तम अभ्यास दिशानिर्देश हैं ?

वे कुछ समान लाभों की पेशकश करने लगते हैं।


2
मैंने स्काला एन्यूमरेशन और विकल्प के बारे में एक छोटा सा विवरण लिखा है, आप इसे उपयोगी पा सकते हैं: pedrorijo.com/blog/scala-enums/
pedrorijo91

1
डॉटी-आधारित स्काला 3enum (2020 के मध्य तक) भी देखें ।
वॉन

जवाबों:


223

एक बड़ा अंतर यह है कि Enumerationउन्हें कुछ nameस्ट्रिंग से तात्कालिक करने के लिए समर्थन के साथ आता है । उदाहरण के लिए:

object Currency extends Enumeration {
   val GBP = Value("GBP")
   val EUR = Value("EUR") //etc.
} 

तो आप कर सकते हैं:

val ccy = Currency.withName("EUR")

यह तब उपयोगी होता है जब एन्यूमरेशन्स (उदाहरण के लिए, डेटाबेस में) को बनाए रखने या फ़ाइलों में रहने वाले डेटा से उन्हें बनाने के लिए उपयोगी होता है। हालाँकि, मुझे सामान्य तौर पर लगता है कि गणना में स्काला में थोड़ा सा अनाड़ी है और एक अजीब ऐड-ऑन की भावना है, इसलिए मैं अब case objectएस का उपयोग करना चाहता हूं । एक 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 (कोड)" पैटर्न के बारे में, Currencyटाइप के बंद सेट प्रकृति को "ब्रेकिंग" की तुलना में मुद्रा कोड स्ट्रिंग नहीं ढूंढने से निपटने के अन्य तरीके हैं । UnknownCurrencyप्रकार Currencyका होना अब एक एपीआई के अन्य भागों में चुपके हो सकता है।

यह सलाह दी जाती है कि उस मामले को बाहर धकेलें Enumerationऔर क्लाइंट डील को एक ऐसे Option[Currency]प्रकार के साथ करें जो स्पष्ट रूप से इंगित करता है कि वास्तव में एक मेल खाने वाली समस्या है और एपीआई के उपयोगकर्ता को उसे "स्वयं" हल करने के लिए "प्रोत्साहित" करें।

अन्य उत्तर यहाँ पर अनुवर्ती कार्रवाई करने के लिए, की मुख्य कमियां case objectअधिक रों Enumerationरों हैं:

  1. "गणन" के सभी उदाहरणों पर पुनरावृति नहीं कर सकते । यह निश्चित रूप से मामला है, लेकिन मैंने इसे अभ्यास में अत्यंत दुर्लभ पाया है कि इसकी आवश्यकता है।

  2. लगातार मूल्य से आसानी से नहीं कर सकते । यह सच भी है लेकिन, विशाल गणनाओं (उदाहरण के लिए, सभी मुद्राओं) के मामले को छोड़कर, यह एक विशाल उपरि प्रस्तुत नहीं करता है।


10
दूसरा अंतर यह है कि एन्यूमरेशन एनम को बॉक्स से बाहर करने का आदेश दिया गया है, जबकि केस ऑब्जेक्ट एनम आधारित नहीं है
om-nom-nom

1
यदि आप जावा इंटरऑपरेबिलिटी की परवाह करते हैं, तो केस ऑब्जेक्ट्स के लिए एक और बिंदु है। Enumeration, Enumeration.Value के रूप में मानों को लौटाएगा, इस प्रकार 1) स्काला-लाइब्रेरी की आवश्यकता है, 2) वास्तविक प्रकार की जानकारी को खोना।
जुआनमीरोक्स

7
@oxbow_lakes बिंदु 1 के बारे में, विशेष रूप से यह हिस्सा "... मैंने इसे व्यवहार में बहुत दुर्लभ पाया है कि यह आवश्यक है": जाहिर है कि आप शायद ही कभी बहुत यूआई काम करते हैं। यह एक अत्यंत सामान्य उपयोग का मामला है; वैध गणन सदस्यों की एक (ड्रॉप-डाउन) सूची प्रदर्शित करना जिसमें से चुनना है।
chaotic3quilibrium

मुझे समझ में नहीं आता trade.ccyहै कि सीलबंद विशेषता छूट में मिलान किए जा रहे आइटम का प्रकार क्या है ।
rloth

और case objectबड़ा (~ 4x) कोड पदचिह्न उत्पन्न नहीं करता है Enumeration? विशेष रूप से scala.jsछोटे पदचिह्न की आवश्यकता वाली परियोजनाओं के लिए उपयोगी अंतर ।
इको सेप

69

अद्यतन: एक नया मैक्रो आधारित समाधान बनाया गया है जो कि मेरे द्वारा बताए गए समाधान से कहीं बेहतर है। मैं इस नए मैक्रो आधारित समाधान का उपयोग करने की दृढ़ता से सलाह देता हूं । और यह प्रतीत होता है कि डोटी के लिए योजनाएं इस शैली को भाषा के समाधान का हिस्सा बनाएंगी। Whoohoo!

सारांश:
जावा Enumको स्केल प्रोजेक्ट के भीतर पुन: पेश करने के प्रयास के लिए तीन बुनियादी पैटर्न हैं । तीन पैटर्न में से दो; सीधे जावा Enumऔर का उपयोग करते हुए scala.Enumeration, स्काला के संपूर्ण पैटर्न मिलान को सक्षम करने में सक्षम नहीं हैं। और तीसरा; "सील विशेषता + केस ऑब्जेक्ट", करता है ... लेकिन जेवीएम वर्ग / ऑब्जेक्ट आरंभीकरण जटिलताओं के परिणामस्वरूप असंगत अनुक्रमणिका पीढ़ी है।

मैंने दो वर्गों के साथ एक समाधान बनाया है; इस जीस्ट में स्थित एन्यूमरेशन और एन्यूमेरेशनडकोरेटेड । मैंने इस थ्रेड में कोड पोस्ट नहीं किया क्योंकि Enumeration के लिए फ़ाइल काफी बड़ी थी (+400 लाइनें - कार्यान्वयन संदर्भ की व्याख्या करने वाली बहुत सारी टिप्पणियाँ हैं)। विवरण: आप जो सवाल पूछ रहे हैं, वह सामान्य है; "... जब कक्षाओं का उपयोग करना बनाम विस्तार करना "। और यह पता चलता है कि आपके पास संभावित उत्तर की विशिष्ट आवश्यकताओं के आधार पर प्रत्येक उत्तर, प्रत्येक संभावित उत्तर हैं। उत्तर को तीन मूल प्रतिमानों तक घटाया जा सकता है।


caseobjects[scala.]Enumeration

शुरू करने के लिए, आइए सुनिश्चित करें कि हम एक ही मूल विचार से काम कर रहे हैं जो एक गणना है। आइए ज्यादातर Enum5 (1.5) के रूप में दिए गए संदर्भ में एक गणना को परिभाषित करें :

  1. इसमें नामित सदस्यों का स्वाभाविक रूप से आदेशित बंद सेट होता है
    1. सदस्यों की एक निश्चित संख्या है
    2. सदस्यों को स्वाभाविक रूप से आदेश दिया जाता है और स्पष्ट रूप से अनुक्रमित किया जाता है
      • जैसा कि कुछ अपर्याप्त सदस्य योग्य मानदंडों के आधार पर हल किया जा रहा है
    3. सभी सदस्यों के कुल सेट के भीतर प्रत्येक सदस्य का एक विशिष्ट नाम होता है
  2. सभी सदस्यों को आसानी से उनके सूचकांक के आधार पर पुनरावृत्त किया जा सकता है
  3. एक सदस्य को इसके (केस संवेदनशील) नाम से पुनः प्राप्त किया जा सकता है
    1. यह काफी अच्छा होगा यदि किसी सदस्य को उसके मामले असंवेदनशील नाम से भी लिया जा सकता है
  4. एक सदस्य को इसके सूचकांक के साथ पुनः प्राप्त किया जा सकता है
  5. सदस्य आसानी से, पारदर्शी और कुशलता से क्रमांकन का उपयोग कर सकते हैं
  6. अतिरिक्त संबद्ध सिंगलटन-नेस डेटा रखने के लिए सदस्यों को आसानी से बढ़ाया जा सकता है
  7. जावा के परे सोचकर Enum, एक प्रतिज्ञान के लिए स्काला के पैटर्न मिलान थकाऊपन को स्पष्ट रूप से लाभ उठाने में सक्षम होना अच्छा होगा

अगला, आइए पोस्ट किए गए तीन सबसे सामान्य समाधान पैटर्न के उबले हुए संस्करणों को देखें:

ए) वास्तव में सीधे जावा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;
  }
}

गणना परिभाषा से निम्नलिखित आइटम उपलब्ध नहीं हैं:

  1. 3.1 - यह काफी अच्छा होगा यदि किसी सदस्य को उसके मामले असंवेदनशील नाम के साथ वापस लिया जा सके
  2. 7 - जावा के एनम से परे सोचकर, यह स्पष्ट रूप से लाभ उठाने में सक्षम होगा कि स्काला का पैटर्न एक थकावट के लिए थकावट की जाँच करता है।

मेरी वर्तमान परियोजनाओं के लिए, मुझे स्काला / जावा मिश्रित परियोजना मार्ग के आसपास जोखिम लेने का लाभ नहीं है। और यहां तक ​​कि अगर मैं एक मिश्रित परियोजना करने का विकल्प चुन सकता हूं, तो आइटम 7 मुझे संकलित समय के मुद्दों को पकड़ने की अनुमति देने के लिए महत्वपूर्ण है, अगर मैं या तो संयुक्ताक्षर सदस्यों को जोड़ / हटा दूं, या मौजूदा गणन सदस्यों से निपटने के लिए कुछ नया कोड लिख रहा हूं।


बी) " 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}
}

गणना परिभाषा से निम्नलिखित आइटम उपलब्ध नहीं हैं:

  1. 1.2 - सदस्यों को स्वाभाविक रूप से आदेश दिया जाता है और स्पष्ट रूप से अनुक्रमित किया जाता है
  2. 2 - सभी सदस्यों को आसानी से उनके सूचकांक के आधार पर पुनरावृत्त किया जा सकता है
  3. 3 - एक सदस्य को इसके (केस संवेदनशील) नाम से पुनः प्राप्त किया जा सकता है
  4. 3.1 - यह काफी अच्छा होगा यदि किसी सदस्य को उसके मामले असंवेदनशील नाम के साथ वापस लिया जा सके
  5. 4 - एक सदस्य को इसके सूचकांक के साथ पुनः प्राप्त किया जा सकता है

यह यकीन है कि यह वास्तव में 5 और 6. 5 के लिए गणना परिभाषा आइटम को पूरा करता है, यह एक दावा है कि यह कुशल है। 6 के लिए, अतिरिक्त संबद्ध सिंगलटन-नेस डेटा को होल्ड करना वास्तव में आसान नहीं है।


सी)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]
}

एन्यूमरेशन परिभाषा से निम्नलिखित आइटम उपलब्ध नहीं हैं (सीधे जावा एनुम का उपयोग करने के लिए सूची के समान होता है):

  1. 3.1 - यह काफी अच्छा होगा यदि किसी सदस्य को उसके मामले असंवेदनशील नाम के साथ वापस लिया जा सके
  2. 7 - जावा के एनम से परे सोचकर, यह स्पष्ट रूप से लाभ उठाने में सक्षम होगा कि स्काला का पैटर्न एक थकावट के लिए थकावट की जाँच करता है।

मेरी वर्तमान परियोजनाओं के लिए, आइटम 7 मुझे संकलित समय के मुद्दों को पकड़ने की अनुमति देने के लिए महत्वपूर्ण है अगर / जब मैं या तो गणना सदस्यों को जोड़ता / हटाता हूं, या मौजूदा गणना सदस्यों से निपटने के लिए कुछ नए कोड लिख रहा हूं।


इसलिए, एक गणना की उपरोक्त परिभाषा को देखते हुए, उपरोक्त तीन समाधानों में से कोई भी काम नहीं करता है क्योंकि वे उपरोक्त गणना परिभाषा में उल्लिखित सब कुछ प्रदान नहीं करते हैं:

  1. जावा Enum सीधे एक मिश्रित स्काला / जावा परियोजना में
  2. "सील विशेषता + मामले वस्तुओं"
  3. scala.Enumeration

इनमें से प्रत्येक समाधान को अंततः प्रत्येक के लापता आवश्यकताओं में से कुछ को कवर करने का प्रयास करने के लिए reworked / विस्तारित / refactored किया जा सकता है। हालाँकि, आइटम 7 प्रदान करने के लिए न तो जावा Enumऔर न ही scala.Enumerationसमाधानों का पर्याप्त विस्तार किया जा सकता है। और मेरी अपनी परियोजनाओं के लिए, यह स्काला के भीतर एक बंद प्रकार का उपयोग करने के अधिक आकर्षक मूल्यों में से एक है। मैं दृढ़ता से संकलन समय / चेतावनियों को प्राथमिकता देना पसंद करता हूं ताकि मुझे संकेत मिल सके कि मेरे कोड में गैप / मुद्दा है और इसे प्रोडक्शन रनटाइम अपवाद / विफलता से अलग करने का विरोध किया है।


उस संबंध में, मैंने case objectयह देखने के लिए मार्ग के साथ काम करने के बारे में निर्धारित किया है कि क्या मैं एक समाधान का उत्पादन कर सकता हूं जिसने उपरोक्त सभी गणना परिभाषा को कवर किया है। पहली चुनौती जेवीएम वर्ग / वस्तु आरंभीकरण मुद्दे ( इस स्टैकऑवरफ्लो में विस्तार से कवर ) के मूल के माध्यम से धक्का था । और मैं आखिरकार एक समाधान निकालने में सक्षम था।

जैसा कि मेरा समाधान दो लक्षण है; 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
}

यह एन्यूमरेशन ट्रेस की एक नई जोड़ी का एक उदाहरण है, जिसे मैंने ( इस जिस्ट में स्थित ) एन्यूमरेशन परिभाषा में वांछित और उल्लिखित सभी क्षमताओं को लागू करने के लिए बनाया है।

एक चिंता व्यक्त की गई है कि गणना सदस्य नामों को ( decorationOrderedSetउदाहरण में ऊपर) दोहराया जाना चाहिए । हालांकि मैंने इसे एक एकल पुनरावृत्ति के लिए कम कर दिया था, मैं यह नहीं देख सकता था कि दो मुद्दों के कारण इसे और भी कम कैसे बनाया जाए:

  1. इस विशेष ऑब्जेक्ट / केस ऑब्जेक्ट मॉडल के लिए जेवीएम ऑब्जेक्ट / क्लास इनिशियलाइज़ेशन अपरिभाषित है ( इस स्टैकओवरफ़्लो थ्रेड को देखें )
  2. विधि से वापस की गई सामग्री getClass.getDeclaredClassesका अपरिभाषित क्रम है (और यह उसी क्रम में होने की संभावना नहीं है जितनी case objectस्रोत कोड में घोषणाएं हैं)

इन दो मुद्दों को देखते हुए, मुझे एक निहित आदेश उत्पन्न करने की कोशिश छोड़नी पड़ी और स्पष्ट रूप से ग्राहक को परिभाषित करने की आवश्यकता थी और इसे किसी प्रकार के आदेशित धारणा के साथ घोषित करना था। जैसा कि स्काला संग्रह में एक सम्मिलित आदेश सेट कार्यान्वयन नहीं है, मैं जो सबसे अच्छा कर सकता था वह था एक का उपयोग करें Listऔर फिर रनटाइम चेक करें कि यह वास्तव में एक सेट था। ऐसा नहीं है कि मैंने इसे कैसे हासिल करना पसंद किया होगा।

और दिए गए डिजाइन इस दूसरी सूची / सेट आदेश की आवश्यकता valको देखते हुए, ChessPiecesEnhancedDecoratedऊपर के उदाहरण, इसे जोड़ने के लिए संभव हो गया था case object PAWN2 extends Memberऔर उसके बाद जोड़ने के लिए भूल जाते हैं Decoration(PAWN2,'P2', 2)करने के लिए decorationOrderedSet। इसलिए, यह सत्यापित करने के लिए एक रनटाइम जांच है कि सूची न केवल एक सेट है, लेकिन इसमें सभी केस ऑब्जेक्ट शामिल हैं जो विस्तार करते हैं sealed trait Member। यह काम करने के लिए प्रतिबिंब / स्थूल नरक का एक विशेष रूप था।


कृपया टिप्पणी और / या Gist पर प्रतिक्रिया दें ।


मैंने अब ScalaOlio लाइब्रेरी (GPLv3) का पहला संस्करण जारी किया है जिसमें दोनों के org.scalaolio.util.Enumerationऔर अधिक अद्यतित संस्करण हैं org.scalaolio.util.EnumerationDecorated: और scalaolio.org
chaotic3quilibrium

और गितुब पर स्कालाओलियो भंडार के लिए सीधे हॉप करने के लिए: github.com/chaotic3quilibrium/scala-olio
chaotic3quilibrium

5
यह एक गुणवत्ता का उत्तर है और इससे बहुत कुछ लेना है। धन्यवाद
angabriel

1
ऐसा लगता है कि ओडस्की एक देशी एनम के साथ डोट्टी (भविष्य स्काला 3.0) को अपग्रेड करना चाहते हैं। Whoohoo! github.com/lampepfl/dotty/issues/1970
chaotic3quilibrium

62

केस ऑब्जेक्ट्स पहले से ही अपने स्ट्रोइंग तरीकों के लिए अपना नाम वापस कर देते हैं, इसलिए इसे अलग से पारित करना अनावश्यक है। यहाँ jho's (संक्षिप्त विधि के लिए छोड़ी गई सुविधा) के समान एक संस्करण है:

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)
}

वस्तुएं आलसी हैं; मान्यताओं का उपयोग करने के बजाय हम सूची को छोड़ सकते हैं लेकिन नाम दोहराना होगा:

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") {}
}

यदि आपको कुछ धोखा देने का मन नहीं है, तो आप प्रतिबिंब एपीआई या 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
}

मामला कक्षाओं और जावा गणना के सभी लाभों के साथ अच्छा और साफ। व्यक्तिगत रूप से, मैं बेहतर आइडियल स्कैला कोड से ऑब्जेक्ट के बाहर की गणना मूल्यों को परिभाषित करता हूं:

object Currency extends Enum[Currency]
sealed trait Currency extends Currency.Value
case object EUR extends Currency
case object GBP extends Currency

3
एक प्रश्न: अंतिम समाधान को "गैर आलसी केस ऑब्जेक्ट" कहा जाता है, लेकिन इस मामले में वस्तुओं को तब तक लोड नहीं किया जाता है जब तक हम उनका उपयोग नहीं करते हैं: आप इस समाधान को गैर आलसी क्यों कहते हैं?
सेब सेब्रॉन

2
@ नहीं, आपको उपयोग करने की आवश्यकता है: पूरे सील किए गए पदानुक्रम को आरईपीएल में चिपकाने के लिए पेस्ट करें। यदि आप नहीं करते हैं, तो एकल फ़ाइल के रूप में सीलबंद बेस क्लास / ट्रिट काउंट वाली सिंगल लाइन को तुरंत सील कर दिया जाता है, और अगली लाइन पर नहीं बढ़ाया जा सकता है।
जर्गेन स्ट्रोबेल

2
@ गेट्सडा केवल आपके पहले कोड स्निपेट में बग नहीं है (जैसा कि आपको स्पष्ट रूप से क्लाइंट को मूल्यों को घोषित करने और परिभाषित करने की आवश्यकता है। आपके दूसरे और तीसरे समाधान दोनों के पास सूक्ष्म बग है जो मैंने अपनी अंतिम टिप्पणी में वर्णित किया है (यदि क्लाइंट मुद्रा का उपयोग करने के लिए होता है) .GBP सीधे और पहले, मानों की सूची "ऑर्डर से बाहर हो जाएगी")। मैंने स्काला एन्यूमरेशन डोमेन की बड़े पैमाने पर खोज की है और इसे इसी सूत्र के जवाब में विस्तार से कवर किया है: stackoverflow.com/a/25923651-501113
chaotic3quilibrium

1
शायद इस दृष्टिकोण की कमियां (जावा एनम की तुलना में वैसे भी) यह है कि जब आप आईडीई में मुद्रा <डॉट> टाइप करते हैं तो यह उपलब्ध विकल्प नहीं दिखाता है।
इवान बालाशोव

1
जैसा कि @SebCesbron ने उल्लेख किया है, केस ऑब्जेक्ट यहां आलसी हैं। इसलिए यदि मैं फोन करता हूं Currency.values, तो मुझे केवल वे मूल्य मिलते हैं जो मैंने पहले एक्सेस किए हैं। क्या उसके आसपास कोई रास्ता है?
सासगोरिल्ला

27

गणना पर मामला वर्गों का उपयोग करने के लाभ हैं:

  • सीलबंद केस क्लासेस का उपयोग करते समय, स्कैला कंपाइलर यह बता सकता है कि मैच पूरी तरह से निर्दिष्ट है या नहीं, जब सभी संभावित मिलान मिलान मिलान में जासूसी करते हैं। गणना के साथ, स्केल संकलक नहीं बता सकता है।
  • केस क्लासेस स्वाभाविक रूप से वैल्यू आधारित एन्यूमरेशन से अधिक फ़ील्ड्स का समर्थन करता है जो एक नाम और आईडी का समर्थन करता है।

केस कक्षाओं के बजाय गणना का उपयोग करने के फायदे हैं:

  • गणना आमतौर पर लिखने के लिए थोड़ा कम कोड होगा।
  • किसी अन्य व्यक्ति के लिए Scalaerations स्कैला के लिए समझना आसान है क्योंकि वे अन्य भाषाओं में प्रचलित हैं

इसलिए सामान्य तौर पर, यदि आपको नाम से सरल स्थिरांक की सूची की आवश्यकता है, तो गणना का उपयोग करें। अन्यथा, यदि आपको कुछ अधिक जटिल की आवश्यकता है या आप चाहते हैं कि संकलक की अतिरिक्त सुरक्षा आपको बताए कि क्या आपके पास सभी मैच निर्दिष्ट हैं, तो केस क्लासेस का उपयोग करें।


15

अद्यतन: नीचे दिए गए कोड में एक बग है, यहां वर्णित है । नीचे दिए गए परीक्षण कार्यक्रम काम करता है, लेकिन यदि आप DayOfWeek से पहले DayOfWeek.Mon (उदाहरण के लिए) का उपयोग करते हैं, तो यह विफल हो जाएगा क्योंकि DayOfWeek को प्रारंभ नहीं किया गया है (किसी आंतरिक ऑब्जेक्ट का उपयोग प्रारंभ करने के लिए किसी बाहरी ऑब्जेक्ट के कारण नहीं होता है)। यदि आप val enums = Seq( DayOfWeek )अपने मुख्य वर्ग में कुछ ऐसा करते हैं, तो आप अपने एनमों की शुरुआत के लिए मजबूर कर सकते हैं, या आप chaotic3quilibrium के संशोधनों का उपयोग कर सकते हैं। मैक्रो-आधारित Enum के लिए आगे देख रहे हैं!


अगर तुम चाहते हो

  • गैर-निकास पैटर्न मैचों के बारे में चेतावनी
  • प्रत्येक एनुम मान को असाइन किया गया एक इंट आईडी, जिसे आप वैकल्पिक रूप से नियंत्रित कर सकते हैं
  • एनम मूल्यों की एक अपरिवर्तनीय सूची, जिस क्रम में उन्हें परिभाषित किया गया था
  • नाम से एनम मूल्य के लिए एक अपरिवर्तनीय मानचित्र
  • आईडी से एनम मूल्य के लिए एक अपरिवर्तनीय मानचित्र
  • सभी या विशेष रूप से एनम मानों के लिए या संपूर्ण रूप से एनम के लिए तरीकों / डेटा को छड़ी करने के लिए स्थान
  • आदेशित एनम मान (ताकि आप परीक्षण कर सकें, उदाहरण के लिए, चाहे दिन <बुधवार)
  • दूसरों को बनाने के लिए एक एनम का विस्तार करने की क्षमता

फिर निम्नलिखित ब्याज की हो सकती है। प्रतिक्रिया का स्वागत करते हैं।

इस कार्यान्वयन में अमूर्त 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()
}

ध्यान दें कि आपको इसे लाने के लिए प्रत्येक एनम मूल्य (इसकी लागू पद्धति को कॉल करें) का उपयोग करना होगा। [मैं चाहता हूं कि आंतरिक वस्तुएं तब तक आलसी नहीं थीं जब तक कि मैं विशेष रूप से उनके लिए नहीं पूछता। मुझे लगता है।]

यदि हम चाहें तो हम निश्चित रूप से 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 class ही है (और इसके भीतर 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 )
    }
  }

}

और यहाँ इसका एक और अधिक उन्नत उपयोग है जो आईडी को नियंत्रित करता है और वैल एब्सट्रैक्शन और एनम को ही डेटा / विधियों को जोड़ता है:

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)
}

इसे प्रदान करने के लिए Tyvm। मैं वास्तव में इसकी प्रशंसा करता हूँ। हालाँकि, मैं देख रहा हूँ कि यह "var" का उपयोग वैल के विपरीत कर रहा है। और यह एफपी दुनिया में एक सीमावर्ती नश्वर पाप है। तो, क्या इसे लागू करने का एक तरीका ऐसा है कि var का कोई उपयोग नहीं है? बस उत्सुक अगर यह एफपी प्रकार के किनारे के मामले का कुछ प्रकार है और मुझे समझ में नहीं आ रहा है कि आपका कार्यान्वयन एफपी अवांछनीय कैसे है।
चॉटिक

2
मैं शायद आपकी मदद नहीं कर सकता। स्काला में उन वर्गों को लिखना आम है जो आंतरिक रूप से उत्परिवर्तित होते हैं लेकिन जो उनका उपयोग करने वालों के लिए अपरिवर्तनीय हैं। उपर्युक्त उदाहरण में, DayOfWeek का उपयोगकर्ता एनम को म्यूट नहीं कर सकता है; इस तथ्य के बाद, उदाहरण के लिए, मंगलवार की आईडी या उसके नाम को बदलने का कोई तरीका नहीं है। लेकिन अगर आप ऐसा कार्यान्वयन चाहते हैं जो आंतरिक रूप से उत्परिवर्तन से मुक्त हो , तो मुझे कुछ नहीं मिला है। मुझे आश्चर्य नहीं होगा, हालांकि, 2.11 में मैक्रोज़ पर आधारित एक अच्छी नई ईनम सुविधा देखने के लिए; विचारों को स्काला-लैंग पर चारों ओर से घसीटा जा रहा है।
अमीगोनिको

मुझे स्काला वर्कशीट में एक अजीब त्रुटि मिल रही है। यदि मैं सीधे मूल्य उदाहरणों में से एक का उपयोग करता हूं, तो मुझे एक प्रारंभिक त्रुटि मिलती है। हालाँकि, अगर मैं एन्यूलेशन की सामग्री को देखने के लिए .values ​​विधि को कॉल करता हूं, तो वह काम करता है और फिर सीधे मूल्य उदाहरण कार्यों का उपयोग करता है। किसी भी विचार है कि आरंभीकरण त्रुटि क्या है? और क्या इष्टतम तरीका यह है कि सम्मेलन को बुलाए जाने के बावजूद प्रारंभिक क्रम उचित क्रम में होता है?
chaotic3quilibrium

@ chaotic3quilibrium: वाह! इसको आगे बढ़ाने के लिए धन्यवाद, और भारी उठाने के लिए रेक्स केर के लिए निश्चित रूप से धन्यवाद। मैं यहां समस्या का उल्लेख करूंगा और आपके द्वारा बनाए गए प्रश्न का संदर्भ लूंगा।
AmigoNico

"" का उपयोग करना var] एफपी दुनिया में एक सीमावर्ती नश्वर पाप है "- मुझे नहीं लगता कि राय सार्वभौमिक रूप से स्वीकार की जाती है।
एरिक कपलुन

12

मेरा यहाँ एक अच्छा सा सरल काम है जो आपको अपने स्वयं के मूल्यों की सूची को बनाए रखने के बिना सीमांत मानों के रूप में सीलबंद लक्षणों / वर्गों का उपयोग करने की अनुमति देता है। यह एक साधारण मैक्रो पर निर्भर करता है जो बग्गी पर निर्भर नहीं है knownDirectSubclasses

https://github.com/lloydmeta/enumeratum


10

अपडेट मार्च 2017: जैसा कि एंथनी एक्सीली द्वारा टिप्पणी की गई है , scala.Enumeration/enumपीआर को बंद कर दिया गया है।

डॉट्टी (स्काला के लिए अगली पीढ़ी के कंपाइलर) प्रमुख भूमिका निभाएंगे , हालांकि डॉटी 1970 और मार्टिन ओडस्की की पीआर 1958


नोट: अब (अगस्त 2016, 6+ साल बाद) एक प्रस्ताव को हटाने के लिए है scala.Enumeration: PR 5352

संक्षिप्त करें scala.Enumeration, @enumएनोटेशन जोड़ें

वाक्य रचना

@enum
 class Toggle {
  ON
  OFF
 }

एक संभावित कार्यान्वयन उदाहरण है, इरादा ADTs का समर्थन करना है जो कुछ प्रतिबंधों (कोई नेस्टिंग, पुनरावृत्ति या अलग-अलग निर्माण मापदंडों) के अनुरूप नहीं है, जैसे:

@enum
sealed trait Toggle
case object ON  extends Toggle
case object OFF extends Toggle

असम्बद्ध आपदा को दर्शाता है scala.Enumeration

Scala पर @enum के फायदे। ज्ञान:

  • वास्तव में काम करता है
  • जावा इंटरॉप
  • कोई मिटाने की बात नहीं
  • गणना को परिभाषित करते समय सीखने के लिए कोई भ्रमित मिनी-डीएसएल नहीं

नुकसान: कोई नहीं।

यह Scala-JVM, Scala.jsऔर Scala-Native (जावा स्रोत कोड पर समर्थित नहीं है Scala.js/Scala-Native, Scala स्रोत कोड उन Enums को परिभाषित करने में सक्षम नहीं है, जो Scala-JVM पर मौजूद API द्वारा स्वीकार नहीं करते हैं) एक कोडबेस नहीं होने के मुद्दे को संबोधित करते हैं।


उपरोक्त पीआर बंद हो गया है (कोई खुशी नहीं)। यह अब 2017 है और ऐसा लग रहा है कि आखिरकार डोट्टी को एक एनम कंस्ट्रक्शन मिलने वाला है। यहां मुद्दा है और मार्टिन का पीआर । मर्ज, मर्ज, मर्ज!
एंथोनी एक्यूली

8

जब आप सभी उदाहरणों में पुनरावृति या फ़िल्टर करने की आवश्यकता होगी, तो केस क्लासेस बनाम एनुमरेशंस का एक और नुकसान। यह Enumeration (और Java enums) की एक अंतर्निहित क्षमता है, जबकि केस क्लासेस स्वचालित रूप से ऐसी क्षमता का समर्थन नहीं करते हैं।

दूसरे शब्दों में: "केस कक्षाओं के साथ गणना किए गए मानों के कुल सेट की सूची प्राप्त करने का कोई आसान तरीका नहीं है"।


5

यदि आप अन्य JVM भाषाओं (जैसे जावा) के साथ अंतर बनाए रखने के बारे में गंभीर हैं तो सबसे अच्छा विकल्प जावा एनम लिखना है। वे स्काला और जावा कोड दोनों से पारदर्शी तरीके से काम करते हैं, जो कि scala.Enumerationवस्तुओं के लिए या केस की तुलना में अधिक है । चलो GitHub पर हर नए शौक परियोजना के लिए एक नई गणना पुस्तकालय नहीं है, अगर इसे टाला जा सकता है!


4

मैंने केस क्लास की नकल बनाने के विभिन्न संस्करणों को देखा है। यहाँ मेरा संस्करण है:

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)
}

हो सकता है कि कोई व्यक्ति मेरे केस की सूची में प्रत्येक केस क्लास को जोड़ने की तुलना में बेहतर चाल के साथ आ सकता है। यह सब मैं उस समय के साथ आ सकता था।


दो अलग-अलग अनुपयोगी तरीके क्यों?
सास

@ जो मैं आपके समाधान के माध्यम से काम करने की कोशिश कर रहा हूं, वह है, लेकिन यह संकलन नहीं करेगा। दूसरे कोड स्निपिट में, "टाइप वी = साइट" में साइट का संदर्भ है। मुझे यकीन नहीं है कि संकलन त्रुटि को स्पष्ट करने के लिए क्या है। अगला, आप "अमूर्त वर्ग मुद्रा" के लिए खाली ब्रेसिज़ क्यों प्रदान कर रहे हैं? क्या उन्हें अभी छोड़ा नहीं जा सकता है? अंत में, आप "var मानों = ..." में किसी var का उपयोग क्यों कर रहे हैं? क्या इसका मतलब यह नहीं है कि ग्राहक कोड में कहीं से भी किसी भी समय मूल्यों पर एक नई सूची प्रदान कर सकते हैं? यह एक var के बजाय एक घाटी बनाने के लिए बहुत बेहतर नहीं होगा?
chaotic3quilibrium

2

मैं पिछले दो बार इन दो विकल्पों पर आगे-पीछे जा रहा हूं, जिनकी मुझे आवश्यकता है। हाल तक तक, मेरी प्राथमिकता सीलबंद विशेषता / केस ऑब्जेक्ट विकल्प के लिए रही है।

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

हालांकि, इनमें से कोई भी वास्तव में सभी को पूरा नहीं करता है जो एक जावा गणन आपको देता है, नीचे पेशेवरों और विपक्ष हैं:

स्काला एन्यूमरेशन

पेशेवरों: विकल्प के साथ तत्काल या सीधे सटीक (आसान जब स्टोर से लोड हो रहा है) के लिए -Functions संभव सभी मूल्यों पर समर्थन है

विपक्ष: गैर-थकाऊ खोज के लिए -कंप्लीशन चेतावनी समर्थित नहीं है (पैटर्न को कम आदर्श बनाता है)

केस ऑब्जेक्ट / सील लक्षण

पेशेवरों: सील किए गए लक्षणों का उपयोग करते हुए, हम कुछ मूल्यों को पूर्व-इंस्टेंट कर सकते हैं, जबकि अन्य को निर्माण के समय-पैटर्न के मिलान के लिए समर्थन में इंजेक्ट किया जा सकता है (लागू / लागू न किए गए तरीके)

विपक्ष: - एक निरंतर स्टोर से अस्थिर करना - आपको अक्सर यहां मिलान पैटर्न का उपयोग करना होगा या सभी संभव 'एनम वैल्यूज' की अपनी सूची को परिभाषित करना होगा।

आखिरकार मुझे क्या पता चला कि मेरी राय निम्न स्निपेट की तरह थी:

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)
  }
}

इसलिए मुझे लगता है कि मेरी प्राथमिकता आगे बढ़ने के लिए एनुमरेशन्स का उपयोग करना है जब मूल्यों को एक रिपॉजिटरी और केस ऑब्जेक्ट्स / सील किए गए लक्षणों से अन्यथा एक्सेस करने का इरादा है।


मैं देख सकता हूं कि दूसरा कोड पैटर्न कैसे वांछनीय है (पहले कोड पैटर्न से दो सहायक तरीकों से छुटकारा पाना)। हालाँकि, मुझे इस तरह से समझ आया कि आप इन दो पैटर्न के बीच चयन करने के लिए मजबूर नहीं हैं। मैं इस सूत्र में पोस्ट किए गए उत्तर में पूरे डोमेन को कवर करता हूं: stackoverflow.com/a/25923651/501113
chaotic3quilibrium

2

मैं पसंद करता हूं 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 }
}

0

उन लोगों के लिए जो अभी भी काम करने के लिए गेट्सडा का जवाब पाने के लिए देख रहे हैं : आप इसे तुरंत घोषित करने के लिए घोषित करने के बाद मामले की वस्तु को संदर्भित कर सकते हैं:

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
}

0

मुझे लगता है कि 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() = ...
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.