मैं स्काला पर टाइप इरेज़र के बारे में कैसे पता करूँ? या, मुझे अपने संग्रह का प्रकार पैरामीटर क्यों नहीं मिल सकता है?


370

यह स्काला पर जीवन का एक दुखद तथ्य है कि यदि आप एक सूची [इंट] को तत्काल भेजते हैं, तो आप यह सत्यापित कर सकते हैं कि आपका उदाहरण एक सूची है, और आप यह सत्यापित कर सकते हैं कि इसका कोई अलग-अलग तत्व एक इंट है, लेकिन यह नहीं कि यह सूची है [ इंट], जैसा कि आसानी से सत्यापित किया जा सकता है:

scala> List(1,2,3) match {
     | case l : List[String] => println("A list of strings?!")
     | case _ => println("Ok")
     | }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!

-Unchecked विकल्प दोष मिटाता है प्रकार प्रकार पर मिटा देता है:

scala>  List(1,2,3) match {
     |  case l : List[String] => println("A list of strings?!")
     |  case _ => println("Ok")
     |  }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
        case l : List[String] => println("A list of strings?!")
                 ^
A list of strings?!

ऐसा क्यों है, और मैं इसके आसपास कैसे पहुंचूं?


स्कैला 2.8 बीटा 1 आरसी 4 ने केवल कुछ बदलाव किए कि किस प्रकार इरेज़र काम करता है। मुझे यकीन नहीं है कि यह सीधे आपके सवाल को प्रभावित करता है।
स्कॉट मॉरिसन

1
यह सिर्फ इतना है कि किस प्रकार को मिटाना है , वह बदल गया है। इसकी कमी को " प्रस्ताव:" ऑब्जेक्ट के बजाय "ऑब्जेक्ट" के बजाय "ए" के रूप में अभिव्यक्त किया जा सकता है । वास्तविक विनिर्देश अधिक जटिल है। यह किसी भी दर पर मिश्रणों के बारे में है, और यह सवाल जेनेरिक के बारे में चिंतित है।
डैनियल सी। सोबरल

स्पष्टीकरण के लिए धन्यवाद - मैं एक नवागंतुक हूं। मुझे लगता है कि अभी स्काला में कूदने का एक बुरा समय है। इससे पहले, मैं 2.8 में बदलाव सीख सकता था, एक अच्छे आधार से, बाद में मुझे कभी भी अंतर नहीं पता होगा!
स्कॉट मॉरिसन

1
यहाँ TypeTags के बारे में कुछ हद तक संबंधित प्रश्न है ।
पॉवरब

2
दौड़ते हुए scala 2.10.2, मैंने इसके बजाय यह चेतावनी देखी: <console>:9: warning: fruitless type test: a value of type List[Int] cannot also be a List[String] (but still might match its erasure) case list: List[String] => println("a list of strings?") ^मुझे आपका प्रश्न और उत्तर बहुत मददगार लगे, लेकिन मुझे यकीन नहीं है कि यह अद्यतन चेतावनी पाठकों के लिए उपयोगी है।
केविन मेरेडिथ

जवाबों:


243

यह उत्तर Manifest-API का उपयोग करता है , जिसे स्केल 2.10 के रूप में दर्शाया गया है। कृपया अधिक वर्तमान समाधानों के लिए नीचे दिए गए उत्तर देखें।

Scala को Type Erasure के साथ परिभाषित किया गया था क्योंकि Java के विपरीत Java Virtual Machine (JVM) को जेनेरिक नहीं मिला था। इसका मतलब यह है कि, रन टाइम पर, केवल क्लास मौजूद है, न कि इसके टाइप पैरामीटर। उदाहरण में, JVM जानता है कि यह एक को संभाल रहा है scala.collection.immutable.List, लेकिन ऐसा नहीं है कि यह सूची के साथ मानकीकृत है Int

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

object Registry {
  import scala.reflect.Manifest

  private var map= Map.empty[Any,(Manifest[_], Any)] 

  def register[T](name: Any, item: T)(implicit m: Manifest[T]) {
    map = map.updated(name, m -> item)
  }

  def get[T](key:Any)(implicit m : Manifest[T]): Option[T] = {
    map get key flatMap {
      case (om, s) => if (om <:< m) Some(s.asInstanceOf[T]) else None
    }     
  }
}

scala> Registry.register("a", List(1,2,3))

scala> Registry.get[List[Int]]("a")
res6: Option[List[Int]] = Some(List(1, 2, 3))

scala> Registry.get[List[String]]("a")
res7: Option[List[String]] = None

जब एक तत्व का भंडारण करते हैं, तो हम इसका एक "मैनिफेस्ट" भी संग्रहीत करते हैं। एक घोषणापत्र एक वर्ग है जिसका उदाहरण स्काला प्रकारों का प्रतिनिधित्व करता है। इन वस्तुओं में जेवीएम की तुलना में अधिक जानकारी है, जो हमें पूर्ण, पैरामीटर प्रकार के लिए परीक्षण करने में सक्षम बनाती है।

हालाँकि, ध्यान दें कि Manifestयह अभी भी एक विकसित करने की सुविधा है। अपनी सीमाओं के उदाहरण के रूप में, यह वर्तमान में विचरण के बारे में कुछ भी नहीं जानता है, और मानता है कि सब कुछ सह-संस्करण है। मुझे उम्मीद है कि स्काला प्रतिबिंब पुस्तकालय, वर्तमान में विकास के तहत, यह और अधिक स्थिर और ठोस हो जाएगा।


3
getविधि के रूप में परिभाषित किया जा सकता for ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
आरोन नोवस्त्रुप

4
@ एरोन बहुत अच्छा सुझाव है, लेकिन मुझे डर है कि यह स्काला के लिए अपेक्षाकृत नए लोगों के लिए कोड को अस्पष्ट कर सकता है। जब मैंने उस प्रश्न / उत्तर में रखा था, तो उस कोड को लिखने से पहले मैं खुद स्कैला के साथ बहुत अनुभव नहीं था।
डेनियल सी। सोबरल

6
@KimStebel आप जानते हैं कि TypeTagवास्तव में पैटर्न मिलान पर स्वचालित रूप से उपयोग किया जाता है? कूल, एह?
डैनियल सी। सोबरल

1
ठंडा! हो सकता है कि आपको जवाब में जोड़ना चाहिए।
किम स्टेबेल

1
मेरे अपने प्रश्न का ठीक ऊपर उत्तर देने के लिए: हां, कंपाइलर Manifestस्वयं ही परम उत्पन्न करता है, देखें: stackoverflow.com/a/11495793/694469 "[प्रकट / टाइप-टैग] उदाहरण [...] संकलक द्वारा अंतर्निहित रूप से बनाया जा रहा है। ”
काजागमनस

96

आप इसे टाइपटैग का उपयोग करके कर सकते हैं (जैसा कि डैनियल पहले से ही उल्लेख करता है, लेकिन मैं इसे स्पष्ट रूप से बताऊंगा):

import scala.reflect.runtime.universe._
def matchList[A: TypeTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if typeOf[A] =:= typeOf[String] => println("A list of strings!")
  case intlist: List[Int @unchecked] if typeOf[A] =:= typeOf[Int] => println("A list of ints!")
}

आप इसे क्लासटैग का उपयोग करके भी कर सकते हैं (जो आपको स्केला-प्रतिबिंबित पर निर्भर होने से बचाता है):

import scala.reflect.{ClassTag, classTag}
def matchList2[A : ClassTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if classTag[A] == classTag[String] => println("A List of strings!")
  case intlist: List[Int @unchecked] if classTag[A] == classTag[Int] => println("A list of ints!")
}

क्लासटैग का उपयोग तब तक किया जा सकता है जब तक आप उम्मीद नहीं करते कि टाइप पैरामीटर Aअपने आप में एक सामान्य प्रकार है।

दुर्भाग्य से यह एक छोटी सी क्रिया है और आपको संकलक चेतावनी को दबाने के लिए @unchecked एनोटेशन की आवश्यकता है। टाइपटेग को भविष्य में कंपाइलर द्वारा स्वचालित रूप से पैटर्न मैच में शामिल किया जा सकता है: https://issues.scala-lang.org/browse/SI-6517


2
अनावश्यक [List String @unchecked]को हटाने के बारे में क्या है क्योंकि यह इस पैटर्न मैच में कुछ भी नहीं जोड़ता है (बस इसका उपयोग case strlist if typeOf[A] =:= typeOf[String] =>करना होगा, या यहां तक ​​कि case _ if typeOf[A] =:= typeOf[String] =>अगर बाध्य चर की आवश्यकता नहीं है case)।
नादेर घनबारी

1
मुझे लगता है कि दिए गए उदाहरण के लिए काम करेंगे, लेकिन मुझे लगता है कि अधिकांश वास्तविक उपयोग तत्वों के प्रकार से लाभ होगा।
tksfz

ऊपर के उदाहरणों में, गार्ड की स्थिति के सामने अनियंत्रित भाग एक डाली नहीं है? क्या आप पहली बार उस ऑब्जेक्ट पर मैचों से गुजरते समय क्लास कास्ट अपवाद नहीं पाएंगे जो एक स्ट्रिंग के लिए डाले जा सकते हैं?
टॉबी

Hm no मुझे विश्वास है कि गार्ड लगाने से पहले कोई कास्ट नहीं है - अनियंत्रित बिट एक नो-ऑप की तरह है जब तक कि कोड को =>निष्पादित नहीं किया जाता है। (और जब आरएचएस पर कोड निष्पादित किया जाता है, तो गार्ड तत्वों के प्रकार पर एक स्थिर गारंटी प्रदान करते हैं। वहां एक डाली हो सकती है, लेकिन यह सुरक्षित है।)
tksfz

क्या यह समाधान महत्वपूर्ण रनटाइम ओवरहेड का उत्पादन करता है?
stanislav.chetvertkov

65

आप आकारहीनTypeable से प्रकार वर्ग का उपयोग कर सकते हैं परिणाम आप के बाद कर रहे हैं पाने के लिए,

नमूना REPL सत्र,

scala> import shapeless.syntax.typeable._
import shapeless.syntax.typeable._

scala> val l1 : Any = List(1,2,3)
l1: Any = List(1, 2, 3)

scala> l1.cast[List[String]]
res0: Option[List[String]] = None

scala> l1.cast[List[Int]]
res1: Option[List[Int]] = Some(List(1, 2, 3))

यह castऑपरेशन जितना संभव हो उतना सटीक wrt erasure होगा, जो कि इन-स्कोप Typeableइंस्टेंस उपलब्ध है।


14
यह ध्यान दिया जाना चाहिए कि "कास्ट" ऑपरेशन पूरी तरह से पूरे संग्रह और उसके सबकोक्लेक्शन के माध्यम से जाएगा और जांच करेगा कि क्या सभी शामिल मूल्य सही प्रकार के हैं। (यानी, l1.cast[List[String]]मोटे तौर पर for (x<-l1) assert(x.isInstanceOf[String]) बड़े डेटास्ट्रक्चर के लिए या यदि कास्ट बहुत बार होता है, तो यह एक अस्वीकार्य माथे हो सकता है।
डॉमिनिक उरुह

16

मैं एक अपेक्षाकृत सरल समाधान के साथ आया था जो सीमित-उपयोग की स्थितियों में पर्याप्त रूप से होता था, अनिवार्य रूप से लपेटे जाने वाले पैरामीटर प्रकार जो रैपर कक्षाओं में टाइप इरेज़र समस्या से ग्रस्त होते थे, जिनका उपयोग मैच स्टेटमेंट में किया जा सकता है।

case class StringListHolder(list:List[String])

StringListHolder(List("str1","str2")) match {
    case holder: StringListHolder => holder.list foreach println
}

इसमें अपेक्षित आउटपुट है और हमारे केस वर्ग की सामग्री को वांछित प्रकार, स्ट्रिंग सूचियों तक सीमित करता है।

अधिक विवरण यहां: http://www.scalafied.com/?p=60


14

स्काला में टाइप इरेज़र इश्यू को दूर करने का एक तरीका है। में मिलान 1 में काबू पाने प्रकार मिटाना और मिलान में काबू पाने प्रकार मिटाना 2 (विचरण) कैसे कोड के लिए कुछ सहायकों विचरण सहित प्रकार, रैप करने के लिए, मिलान के लिए में से कुछ स्पष्टीकरण रहे हैं।


यह प्रकार के क्षरण को दूर नहीं करता है। उनके उदाहरण में, वैल एक्स कर रहा है: कोई भी = सूची (1,2,3); x मैच {मामला IntList (l) => println (s "मैच $ {l (1)}"); मामला _ => प्रिंट्लन ("नो मैच")} "नो मैच" का निर्माण करता है
user48956

आप 2.10 मैक्रोज़ पर एक नज़र डाल सकते हैं।
एलेक्स

11

मुझे अन्यथा भयानक भाषा की इस सीमा के लिए थोड़ा बेहतर समाधान मिला।

स्काला में, एरेस के साथ प्रकार के क्षरण का मुद्दा नहीं होता है। मुझे लगता है कि इसे एक उदाहरण के साथ प्रदर्शित करना आसान है।

हम कहते हैं कि हमारे पास एक सूची है (Int, String), तो निम्नलिखित एक प्रकार की चेतावनी देता है

x match {
  case l:List[(Int, String)] => 
  ...
}

इसके आसपास काम करने के लिए, पहले एक केस क्लास बनाएं:

case class IntString(i:Int, s:String)

फिर पैटर्न मिलान में कुछ ऐसा करें:

x match {
  case a:Array[IntString] => 
  ...
}

जो पूरी तरह से काम करने लगता है।

इसके लिए सूचियों के बजाय सरणियों के साथ काम करने के लिए आपके कोड में मामूली बदलाव की आवश्यकता होगी, लेकिन एक बड़ी समस्या नहीं होनी चाहिए।

ध्यान दें कि उपयोग करने case a:Array[(Int, String)]से अभी भी एक प्रकार का क्षरण चेतावनी देगा, इसलिए एक नए कंटेनर वर्ग (इस उदाहरण में IntString) का उपयोग करना आवश्यक है ।


10
"अन्यथा भयानक भाषा की सीमा" यह स्काला की कम सीमा और जेवीएम की अधिक सीमा है। शायद स्काला को टाइप जानकारी शामिल करने के लिए डिज़ाइन किया जा सकता था क्योंकि यह JVM पर चलता था, लेकिन मुझे नहीं लगता कि इस तरह का कोई डिज़ाइन जावा के साथ इंटरऑपरेबिलिटी को संरक्षित करता होगा (यानी, जैसा कि डिज़ाइन किया गया है, आप जावा से स्काला को कॉल कर सकते हैं।)
कार्ल जी।

1
अनुवर्ती के रूप में, .NET / CLR में Scala के लिए पुनरीक्षित जेनरिक का समर्थन एक सतत संभावना है।
कार्ल जी

6

चूंकि जावा वास्तविक तत्व प्रकार को नहीं जानता है, इसलिए मैंने इसे केवल उपयोग करने के लिए सबसे उपयोगी पाया List[_]। फिर चेतावनी दूर हो जाती है और कोड वास्तविकता का वर्णन करता है - यह कुछ अज्ञात की सूची है।


4

मैं सोच रहा था कि क्या यह एक अनुकूल समाधान है:

scala> List(1,2,3) match {
     |    case List(_: String, _*) => println("A list of strings?!")
     |    case _ => println("Ok")
     | }

यह "खाली सूची" मामले से मेल नहीं खाता है, लेकिन यह एक संकलित त्रुटि देता है, चेतावनी नहीं!

error: type mismatch;
found:     String
requirerd: Int

दूसरी तरफ यह काम करने लगता है ...।

scala> List(1,2,3) match {
     |    case List(_: Int, _*) => println("A list of ints")
     |    case _ => println("Ok")
     | }

क्या यह थोड़े भी बेहतर नहीं है या मैं इस बिंदु को याद नहीं कर रहा हूँ?


3
सूची (1, "a", "b") के साथ काम नहीं करता है, जिसमें टाइप सूची [कोई भी] है
सुलिवन-

1
यद्यपि सुलिवान की बात सही है और विरासत के साथ संबंधित समस्याएं हैं, फिर भी मुझे यह उपयोगी लगा।
सेठ


0

मैं एक जवाब जोड़ना चाहता था जो इस समस्या को सामान्य करता है: रनटाइम के दौरान मेरी सूची के प्रकार का एक स्ट्रिंग प्रतिनिधित्व कैसे प्राप्त करें

import scala.reflect.runtime.universe._

def whatListAmI[A : TypeTag](list : List[A]) = {
    if (typeTag[A] == typeTag[java.lang.String]) // note that typeTag[String] does not match due to type alias being a different type
        println("its a String")
    else if (typeTag[A] == typeTag[Int])
        println("its a Int")

    s"A List of ${typeTag[A].tpe.toString}"
}

val listInt = List(1,2,3)
val listString = List("a", "b", "c")

println(whatListAmI(listInt))
println(whatListAmI(listString))

-18

पैटर्न मैच गार्ड का उपयोग करना

    list match  {
        case x:List if x.isInstanceOf(List[String]) => do sth
        case x:List if x.isInstanceOf(List[Int]) => do sth else
     }

4
यह काम नहीं करेगा इसका कारण यह है कि isInstanceOfजेवीएम के लिए उपलब्ध जानकारी के आधार पर एक रनटाइम चेक किया जाता है। और उस रनटाइम जानकारी में प्रकार तर्क नहीं होगा List(क्योंकि प्रकार के क्षरण के कारण)।
डोमिनिक Unruh
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.