2015-11-07 5 views
28

मैं स्ट्रिंग्स (जैसे एक csv लाइन) से सरल स्काला मामले कक्षाएं प्रारंभ करने में संक्षिप्त कोड की तलाश:, तार से सरल स्काला मामले कक्षाओं का निर्माण कड़ाई से बॉयलर-प्लेट के बिना

case class Person(name: String, age: Double) 
case class Book(title: String, author: String, year: Int) 
case class Country(name: String, population: Int, area: Double) 

val amy = Creator.create[Person]("Amy,54.2") 
val fred = Creator.create[Person]("Fred,23") 
val hamlet = Creator.create[Book]("Hamlet,Shakespeare,1600") 
val finland = Creator.create[Country]("Finland,4500000,338424") 

सरल Creator यह करने के लिए वस्तु क्या है ? मैं स्कैला के बारे में बहुत अच्छा समाधान देखने से बहुत कुछ सीखूंगा।

(ध्यान दें कि साथी वस्तुओं Person, Book और Country नहीं मौजूद करने के लिए मजबूर किया जाना है। यही कारण है कि बॉयलर-प्लेट होगा चाहिए!)

उत्तर

46

मैं एक समाधान है कि के बारे में के रूप में सरल रूप में आप प्राप्त कर सकते हैं देने के लिए जा रहा हूँ प्रकार सुरक्षा (कोई क्रम अपवाद नहीं, कोई क्रम प्रतिबिंब, आदि) के बारे में कुछ उचित की कमी को देखते हुए सामान्य व्युत्पत्ति के लिए Shapeless का उपयोग कर:

import scala.util.Try 
import shapeless._ 

trait Creator[A] { def apply(s: String): Option[A] } 

object Creator { 
    def create[A](s: String)(implicit c: Creator[A]): Option[A] = c(s) 

    def instance[A](parse: String => Option[A]): Creator[A] = new Creator[A] { 
    def apply(s: String): Option[A] = parse(s) 
    } 

    implicit val stringCreate: Creator[String] = instance(Some(_)) 
    implicit val intCreate: Creator[Int] = instance(s => Try(s.toInt).toOption) 
    implicit val doubleCreate: Creator[Double] = 
    instance(s => Try(s.toDouble).toOption) 

    implicit val hnilCreator: Creator[HNil] = 
    instance(s => if (s.isEmpty) Some(HNil) else None) 

    private[this] val NextCell = "^([^,]+)(?:,(.+))?$".r 

    implicit def hconsCreate[H: Creator, T <: HList: Creator]: Creator[H :: T] = 
    instance { 
     case NextCell(cell, rest) => for { 
     h <- create[H](cell) 
     t <- create[T](Option(rest).getOrElse("")) 
     } yield h :: t 
     case _ => None 
    } 

    implicit def caseClassCreate[C, R <: HList](implicit 
    gen: Generic.Aux[C, R], 
    rc: Creator[R] 
): Creator[C] = instance(s => rc(s).map(gen.from)) 
} 

निर्दिष्ट बिल्कुल के रूप में (हालांकि नोट करें कि मान Option में लिपटे रहे हैं प्रतिनिधित्व करने के लिए इस काम तथ्य यह है कि टी वह आपरेशन को पार्स असफल हो सकता है):

scala> case class Person(name: String, age: Double) 
defined class Person 

scala> case class Book(title: String, author: String, year: Int) 
defined class Book 

scala> case class Country(name: String, population: Int, area: Double) 
defined class Country 

scala> val amy = Creator.create[Person]("Amy,54.2") 
amy: Option[Person] = Some(Person(Amy,54.2)) 

scala> val fred = Creator.create[Person]("Fred,23") 
fred: Option[Person] = Some(Person(Fred,23.0)) 

scala> val hamlet = Creator.create[Book]("Hamlet,Shakespeare,1600") 
hamlet: Option[Book] = Some(Book(Hamlet,Shakespeare,1600)) 

scala> val finland = Creator.create[Country]("Finland,4500000,338424") 
finland: Option[Country] = Some(Country(Finland,4500000,338424.0)) 

यहाँ Creator एक प्रकार वर्ग कि सबूत नहीं है कि हम किसी दिए गए प्रकार में एक स्ट्रिंग पार्स कर सकते हैं प्रदान करता है। हमें String, Int इत्यादि जैसे मूल प्रकारों के लिए स्पष्ट उदाहरण प्रदान करना होगा, लेकिन हम केस वर्गों के लिए जेनेरिक रूप से उदाहरण प्राप्त करने के लिए बेकार का उपयोग कर सकते हैं (मानते हैं कि हमारे पास उनके सभी सदस्य प्रकारों के लिए Creator उदाहरण हैं)।

+2

यह बहुत अच्छा ट्रैविस है! क्या आप इस नोटेशन को थोड़ा सा निर्माता [एच :: टी] 'समझा सकते हैं? मैं यहां किस प्रकार पढ़ूं? – marios

+3

यह पढ़ता है "एचएलिस्ट जिसका सिर एच प्रकार का है और जिसका पूंछ प्रकार टी है"। टी या तो एक और प्रकार का स्तर विपक्ष (एच 2 :: टी 2) या एचएनआईएल यह इंगित करने के लिए है कि उस सूची में कोई और मूल्य नहीं है। –

+0

धन्यवाद निकोलस। एचएलआईस्ट एक बेकार निर्माण है? – marios

2
object Creator { 
    def create[T: ClassTag](params: String): T = { 
    val ctor = implicitly[ClassTag[T]].runtimeClass.getConstructors.head 
    val types = ctor.getParameterTypes 

    val paramsArray = params.split(",").map(_.trim) 

    val paramsWithTypes = paramsArray zip types 

    val parameters = paramsWithTypes.map { 
     case (param, clas) => 
     clas.getName match { 
      case "int" => param.toInt.asInstanceOf[Object] // needed only for AnyVal types 
      case "double" => param.toDouble.asInstanceOf[Object] // needed only for AnyVal types 
      case _ => 
      val paramConstructor = clas.getConstructor(param.getClass) 
      paramConstructor.newInstance(param).asInstanceOf[Object] 
     } 

    } 

    val r = ctor.newInstance(parameters: _*) 
    r.asInstanceOf[T] 
    } 
} 
+1

मुझे वास्तव में यह तथ्य पसंद है कि यह पैकेज सरलता के बिना सरल है। बेशक यह प्रतिबिंब के कारण धीमा हो जाएगा --- लेकिन मुझे वर्तमान उद्देश्यों के बारे में परवाह नहीं है। (मुझे लगता है कि आकारहीन-आधारित उत्तर प्रतिबिंब के बिना संकलन समय पर जादू करता है ...?!) –

+2

@PerfectTiling इस दृष्टिकोण के साथ प्राथमिक समस्या यह है कि यह नाजुक है और जब यह टूट जाता है, तो यह रनटाइम पर टूट जाता है- ज्यादातर मामलों में प्रदर्शन लागत महत्वपूर्ण नहीं है। और ठीक है, बेकार समाधान में कोई रनटाइम प्रतिबिंब नहीं है (हालांकि यह कुछ चर्चा के लिए प्रतिबिंब का उपयोग करता है - मेरा जवाब [यहां] (http://stackoverflow.com/a/33580411/334519) देखें। –

संबंधित मुद्दे