ฉันต้องการรับประเภทของตัวแปรที่รันไทม์ ฉันต้องทำอย่างไร
ฉันต้องการรับประเภทของตัวแปรที่รันไทม์ ฉันต้องทำอย่างไร
คำตอบ:
ดังนั้นพูดอย่างเคร่งครัด "ประเภทของตัวแปร" จะปรากฏอยู่เสมอและสามารถส่งผ่านเป็นพารามิเตอร์ประเภทได้ ตัวอย่างเช่น:
val x = 5
def f[T](v: T) = v
f(x) // T is Int, the type of x
แต่ขึ้นอยู่กับสิ่งที่คุณต้องการทำนั่นจะไม่ช่วยคุณ ตัวอย่างเช่นอาจไม่ต้องการทราบว่าประเภทของตัวแปรคืออะไร แต่ต้องการทราบว่าประเภทของค่าเป็นประเภทเฉพาะเช่นนี้หรือไม่:
val x: Any = 5
def f[T](v: T) = v match {
case _: Int => "Int"
case _: String => "String"
case _ => "Unknown"
}
f(x)
ที่นี่ไม่สำคัญว่าตัวแปรจะเป็นประเภทAnyใด สิ่งที่สำคัญสิ่งที่ตรวจสอบคือประเภทของ5ค่า ในความเป็นจริงTไม่มีประโยชน์ - คุณอาจเขียนมันdef f(v: Any)แทนก็ได้เช่นกัน นอกจากนี้สิ่งนี้ใช้อย่างใดอย่างหนึ่งClassTagหรือค่าClassซึ่งอธิบายไว้ด้านล่างและไม่สามารถตรวจสอบพารามิเตอร์ประเภทของประเภท: คุณสามารถตรวจสอบได้ว่าบางสิ่งเป็นList[_]( Listของบางสิ่ง) หรือไม่ แต่ไม่ว่าจะเป็นเช่น a List[Int]หรือList[String].
ความเป็นไปได้อีกประการหนึ่งคือคุณต้องการระบุประเภทของตัวแปรอีกครั้ง นั่นคือคุณต้องการแปลงชนิดเป็นค่าเพื่อให้คุณสามารถเก็บไว้ผ่านมันไปรอบ ๆ ฯลฯ นี้เกี่ยวข้องกับการสะท้อนและคุณจะใช้อย่างใดอย่างหนึ่งหรือClassTag TypeTagตัวอย่างเช่น:
val x: Any = 5
import scala.reflect.ClassTag
def f[T](v: T)(implicit ev: ClassTag[T]) = ev.toString
f(x) // returns the string "Any"
ยังจะช่วยให้คุณใช้พารามิเตอร์ชนิดที่คุณได้รับในClassTag matchสิ่งนี้ใช้ไม่ได้:
def f[A, B](a: A, b: B) = a match {
case _: B => "A is a B"
case _ => "A is not a B"
}
แต่สิ่งนี้จะ:
val x = 'c'
val y = 5
val z: Any = 5
import scala.reflect.ClassTag
def f[A, B: ClassTag](a: A, b: B) = a match {
case _: B => "A is a B"
case _ => "A is not a B"
}
f(x, y) // A (Char) is not a B (Int)
f(x, z) // A (Char) is a B (Any)
ที่นี่ฉันใช้ไวยากรณ์ขอบเขตบริบทB : ClassTagซึ่งทำงานเหมือนกับพารามิเตอร์นัยในClassTagตัวอย่างก่อนหน้านี้แต่ใช้ตัวแปรที่ไม่ระบุชื่อ
เราสามารถรับ a ClassTagจากค่าได้Classเช่นนี้:
val x: Any = 5
val y = 5
import scala.reflect.ClassTag
def f(a: Any, b: Any) = {
val B = ClassTag(b.getClass)
ClassTag(a.getClass) match {
case B => "a is the same class as b"
case _ => "a is not the same class as b"
}
}
f(x, y) == f(y, x) // true, a is the same class as b
A ClassTagมีข้อ จำกัด ที่ครอบคลุมเฉพาะคลาสพื้นฐานเท่านั้น แต่ไม่ครอบคลุมพารามิเตอร์ประเภท นั่นคือClassTagสำหรับList[Int]และเป็นเหมือนกันList[String] Listหากคุณต้องการพารามิเตอร์ประเภทคุณต้องใช้TypeTagแทน TypeTagแต่ไม่สามารถหาได้จากค่าหรือสามารถที่จะนำมาใช้ในการแข่งขันรูปแบบเนื่องจาก JVM ของการลบออก
ตัวอย่างที่มีTypeTagอาจค่อนข้างซับซ้อน - การไม่เปรียบเทียบแท็กสองประเภทนั้นไม่ใช่เรื่องง่ายอย่างที่เห็นด้านล่าง:
import scala.reflect.runtime.universe.TypeTag
def f[A, B](a: A, b: B)(implicit evA: TypeTag[A], evB: TypeTag[B]) = evA == evB
type X = Int
val x: X = 5
val y = 5
f(x, y) // false, X is not the same type as Int
แน่นอนว่ามีหลายวิธีที่จะทำให้การเปรียบเทียบนั้นกลับมาเป็นจริงได้ แต่ต้องใช้หนังสือสองสามบทเพื่อให้ครอบคลุมจริงๆTypeTagฉันจะหยุดตรงนี้
สุดท้ายบางทีคุณอาจไม่สนใจประเภทของตัวแปรเลย บางทีคุณอาจแค่อยากรู้ว่าคลาสของค่าคืออะไรซึ่งในกรณีนี้คำตอบนั้นค่อนข้างง่าย:
val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it
อย่างไรก็ตามจะเป็นการดีกว่าหากเจาะจงมากขึ้นเกี่ยวกับสิ่งที่คุณต้องการทำให้สำเร็จเพื่อให้คำตอบตรงประเด็นมากขึ้น
Intเป็นAnyแต่ไม่ได้เป็นAny Intมันใช้งานได้กับ Scala 2.10 และควรใช้กับ Scala 2.11 และฉันไม่รู้ว่าทำไมถึงไม่เป็นเช่นนั้น
a match { case _: B => ...ทดสอบประเภทของค่าจริงของตัวแปรไม่ประเภทของตัวแปรa aคุณพูดถูกที่มันจะส่งคืนสิ่งที่คุณพูดใน scala 2.10.6 แต่น่าจะเป็นบั๊ก ใน scala 2.11.8 จะมีการทดสอบประเภทของค่าจริงตามที่ควร
ฉันคิดว่าคำถามไม่สมบูรณ์ หากคุณหมายถึงว่าคุณต้องการรับข้อมูลประเภทของคลาสประเภทบางประเภทจากด้านล่าง:
หากคุณต้องการพิมพ์ตามที่ระบุไว้:
scala> def manOf[T: Manifest](t: T): Manifest[T] = manifest[T]
manOf: [T](t: T)(implicit evidence$1: Manifest[T])Manifest[T]
scala> val x = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> println(manOf(x))
scala.collection.immutable.List[Int]
หากคุณอยู่ในโหมดจำลองแล้ว
scala> :type List(1,2,3)
List[Int]
หรือหากคุณต้องการทราบว่าประเภทของชั้นเรียนเป็นอย่างไรตามที่ @monkjack อธิบายไว้"string".getClassอาจช่วยแก้จุดประสงค์ได้
typeof xนี่manOf(x)คือประเภทข้อมูล!
หากตามประเภทของตัวแปรคุณหมายถึงคลาสรันไทม์ของอ็อบเจ็กต์ที่ตัวแปรชี้ไปคุณจะได้รับสิ่งนี้ผ่านการอ้างอิงคลาสที่อ็อบเจ็กต์ทั้งหมดมี
val name = "sam";
name: java.lang.String = sam
name.getClass
res0: java.lang.Class[_] = class java.lang.String
อย่างไรก็ตามหากคุณหมายถึงประเภทที่มีการประกาศตัวแปรคุณจะไม่สามารถรับสิ่งนั้นได้ เช่นถ้าคุณพูด
val name: Object = "sam"
จากนั้นคุณจะยังคงได้รับStringคืนจากรหัสด้านบน
name.getClass.getSimpleNameเพื่อให้ได้ผลลัพธ์ที่อ่านได้มากขึ้น
ฉันได้ทดสอบแล้วและได้ผล
val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
5เป็นทั้งตัวอย่างของและเป็นตัวอย่างของIntAnyนอกเหนือจากนั้นคำอธิบายของคุณก็สมบูรณ์แบบ :)