क्या स्कैला में प्रसार संख्या बनाम केस कक्षाओं (या केस ऑब्जेक्ट्स) का उपयोग करने के लिए कोई सर्वोत्तम अभ्यास दिशानिर्देश हैं ?
वे कुछ समान लाभों की पेशकश करने लगते हैं।
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
एस का उपयोग करना चाहता हूं । एक 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
रों हैं:
"गणन" के सभी उदाहरणों पर पुनरावृति नहीं कर सकते । यह निश्चित रूप से मामला है, लेकिन मैंने इसे अभ्यास में अत्यंत दुर्लभ पाया है कि इसकी आवश्यकता है।
लगातार मूल्य से आसानी से नहीं कर सकते । यह सच भी है लेकिन, विशाल गणनाओं (उदाहरण के लिए, सभी मुद्राओं) के मामले को छोड़कर, यह एक विशाल उपरि प्रस्तुत नहीं करता है।
trade.ccy
है कि सीलबंद विशेषता छूट में मिलान किए जा रहे आइटम का प्रकार क्या है ।
case
object
बड़ा (~ 4x) कोड पदचिह्न उत्पन्न नहीं करता है Enumeration
? विशेष रूप से scala.js
छोटे पदचिह्न की आवश्यकता वाली परियोजनाओं के लिए उपयोगी अंतर ।
अद्यतन: एक नया मैक्रो आधारित समाधान बनाया गया है जो कि मेरे द्वारा बताए गए समाधान से कहीं बेहतर है। मैं इस नए मैक्रो आधारित समाधान का उपयोग करने की दृढ़ता से सलाह देता हूं । और यह प्रतीत होता है कि डोटी के लिए योजनाएं इस शैली को भाषा के समाधान का हिस्सा बनाएंगी। Whoohoo!
सारांश:
जावा Enum
को स्केल प्रोजेक्ट के भीतर पुन: पेश करने के प्रयास के लिए तीन बुनियादी पैटर्न हैं । तीन पैटर्न में से दो; सीधे जावा Enum
और का उपयोग करते हुए scala.Enumeration
, स्काला के संपूर्ण पैटर्न मिलान को सक्षम करने में सक्षम नहीं हैं। और तीसरा; "सील विशेषता + केस ऑब्जेक्ट", करता है ... लेकिन जेवीएम वर्ग / ऑब्जेक्ट आरंभीकरण जटिलताओं के परिणामस्वरूप असंगत अनुक्रमणिका पीढ़ी है।
मैंने दो वर्गों के साथ एक समाधान बनाया है; इस जीस्ट में स्थित एन्यूमरेशन और एन्यूमेरेशनडकोरेटेड । मैंने इस थ्रेड में कोड पोस्ट नहीं किया क्योंकि Enumeration के लिए फ़ाइल काफी बड़ी थी (+400 लाइनें - कार्यान्वयन संदर्भ की व्याख्या करने वाली बहुत सारी टिप्पणियाँ हैं)।
विवरण:
आप जो सवाल पूछ रहे हैं, वह सामान्य है; "... जब कक्षाओं का उपयोग करना बनाम विस्तार करना "। और यह पता चलता है कि आपके पास संभावित उत्तर की विशिष्ट आवश्यकताओं के आधार पर प्रत्येक उत्तर, प्रत्येक संभावित उत्तर हैं। उत्तर को तीन मूल प्रतिमानों तक घटाया जा सकता है।
case
objects
[scala.]Enumeration
शुरू करने के लिए, आइए सुनिश्चित करें कि हम एक ही मूल विचार से काम कर रहे हैं जो एक गणना है। आइए ज्यादातर Enum
5 (1.5) के रूप में दिए गए संदर्भ में एक गणना को परिभाषित करें :
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;
}
}
गणना परिभाषा से निम्नलिखित आइटम उपलब्ध नहीं हैं:
मेरी वर्तमान परियोजनाओं के लिए, मुझे स्काला / जावा मिश्रित परियोजना मार्ग के आसपास जोखिम लेने का लाभ नहीं है। और यहां तक कि अगर मैं एक मिश्रित परियोजना करने का विकल्प चुन सकता हूं, तो आइटम 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}
}
गणना परिभाषा से निम्नलिखित आइटम उपलब्ध नहीं हैं:
यह यकीन है कि यह वास्तव में 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]
}
एन्यूमरेशन परिभाषा से निम्नलिखित आइटम उपलब्ध नहीं हैं (सीधे जावा एनुम का उपयोग करने के लिए सूची के समान होता है):
मेरी वर्तमान परियोजनाओं के लिए, आइटम 7 मुझे संकलित समय के मुद्दों को पकड़ने की अनुमति देने के लिए महत्वपूर्ण है अगर / जब मैं या तो गणना सदस्यों को जोड़ता / हटाता हूं, या मौजूदा गणना सदस्यों से निपटने के लिए कुछ नए कोड लिख रहा हूं।
इसलिए, एक गणना की उपरोक्त परिभाषा को देखते हुए, उपरोक्त तीन समाधानों में से कोई भी काम नहीं करता है क्योंकि वे उपरोक्त गणना परिभाषा में उल्लिखित सब कुछ प्रदान नहीं करते हैं:
इनमें से प्रत्येक समाधान को अंततः प्रत्येक के लापता आवश्यकताओं में से कुछ को कवर करने का प्रयास करने के लिए 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
उदाहरण में ऊपर) दोहराया जाना चाहिए । हालांकि मैंने इसे एक एकल पुनरावृत्ति के लिए कम कर दिया था, मैं यह नहीं देख सकता था कि दो मुद्दों के कारण इसे और भी कम कैसे बनाया जाए:
getClass.getDeclaredClasses
का अपरिभाषित क्रम है (और यह उसी क्रम में होने की संभावना नहीं है जितनी case object
स्रोत कोड में घोषणाएं हैं)इन दो मुद्दों को देखते हुए, मुझे एक निहित आदेश उत्पन्न करने की कोशिश छोड़नी पड़ी और स्पष्ट रूप से ग्राहक को परिभाषित करने की आवश्यकता थी और इसे किसी प्रकार के आदेशित धारणा के साथ घोषित करना था। जैसा कि स्काला संग्रह में एक सम्मिलित आदेश सेट कार्यान्वयन नहीं है, मैं जो सबसे अच्छा कर सकता था वह था एक का उपयोग करें List
और फिर रनटाइम चेक करें कि यह वास्तव में एक सेट था। ऐसा नहीं है कि मैंने इसे कैसे हासिल करना पसंद किया होगा।
और दिए गए डिजाइन इस दूसरी सूची / सेट आदेश की आवश्यकता 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
केस ऑब्जेक्ट्स पहले से ही अपने स्ट्रोइंग तरीकों के लिए अपना नाम वापस कर देते हैं, इसलिए इसे अलग से पारित करना अनावश्यक है। यहाँ 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
Currency.values
, तो मुझे केवल वे मूल्य मिलते हैं जो मैंने पहले एक्सेस किए हैं। क्या उसके आसपास कोई रास्ता है?
गणना पर मामला वर्गों का उपयोग करने के लाभ हैं:
केस कक्षाओं के बजाय गणना का उपयोग करने के फायदे हैं:
इसलिए सामान्य तौर पर, यदि आपको नाम से सरल स्थिरांक की सूची की आवश्यकता है, तो गणना का उपयोग करें। अन्यथा, यदि आपको कुछ अधिक जटिल की आवश्यकता है या आप चाहते हैं कि संकलक की अतिरिक्त सुरक्षा आपको बताए कि क्या आपके पास सभी मैच निर्दिष्ट हैं, तो केस क्लासेस का उपयोग करें।
अद्यतन: नीचे दिए गए कोड में एक बग है, यहां वर्णित है । नीचे दिए गए परीक्षण कार्यक्रम काम करता है, लेकिन यदि आप 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)
}
var
] एफपी दुनिया में एक सीमावर्ती नश्वर पाप है "- मुझे नहीं लगता कि राय सार्वभौमिक रूप से स्वीकार की जाती है।
मेरा यहाँ एक अच्छा सा सरल काम है जो आपको अपने स्वयं के मूल्यों की सूची को बनाए रखने के बिना सीमांत मानों के रूप में सीलबंद लक्षणों / वर्गों का उपयोग करने की अनुमति देता है। यह एक साधारण मैक्रो पर निर्भर करता है जो बग्गी पर निर्भर नहीं है knownDirectSubclasses
।
अपडेट मार्च 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 द्वारा स्वीकार नहीं करते हैं) एक कोडबेस नहीं होने के मुद्दे को संबोधित करते हैं।
जब आप सभी उदाहरणों में पुनरावृति या फ़िल्टर करने की आवश्यकता होगी, तो केस क्लासेस बनाम एनुमरेशंस का एक और नुकसान। यह Enumeration (और Java enums) की एक अंतर्निहित क्षमता है, जबकि केस क्लासेस स्वचालित रूप से ऐसी क्षमता का समर्थन नहीं करते हैं।
दूसरे शब्दों में: "केस कक्षाओं के साथ गणना किए गए मानों के कुल सेट की सूची प्राप्त करने का कोई आसान तरीका नहीं है"।
यदि आप अन्य JVM भाषाओं (जैसे जावा) के साथ अंतर बनाए रखने के बारे में गंभीर हैं तो सबसे अच्छा विकल्प जावा एनम लिखना है। वे स्काला और जावा कोड दोनों से पारदर्शी तरीके से काम करते हैं, जो कि scala.Enumeration
वस्तुओं के लिए या केस की तुलना में अधिक है । चलो 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)
}
हो सकता है कि कोई व्यक्ति मेरे केस की सूची में प्रत्येक केस क्लास को जोड़ने की तुलना में बेहतर चाल के साथ आ सकता है। यह सब मैं उस समय के साथ आ सकता था।
मैं पिछले दो बार इन दो विकल्पों पर आगे-पीछे जा रहा हूं, जिनकी मुझे आवश्यकता है। हाल तक तक, मेरी प्राथमिकता सीलबंद विशेषता / केस ऑब्जेक्ट विकल्प के लिए रही है।
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)
}
}
इसलिए मुझे लगता है कि मेरी प्राथमिकता आगे बढ़ने के लिए एनुमरेशन्स का उपयोग करना है जब मूल्यों को एक रिपॉजिटरी और केस ऑब्जेक्ट्स / सील किए गए लक्षणों से अन्यथा एक्सेस करने का इरादा है।
मैं पसंद करता हूं 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 }
}
उन लोगों के लिए जो अभी भी काम करने के लिए गेट्सडा का जवाब पाने के लिए देख रहे हैं : आप इसे तुरंत घोषित करने के लिए घोषित करने के बाद मामले की वस्तु को संदर्भित कर सकते हैं:
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() = ...
}