Saya ingin mendapatkan jenis variabel saat runtime. Bagaimana saya melakukan ini?
Jawaban:
Jadi, secara tegas, "tipe variabel" selalu ada, dan dapat diteruskan sebagai parameter tipe. Sebagai contoh:
val x = 5
def f[T](v: T) = v
f(x) // T is Int, the type of x
Tetapi tergantung pada apa yang ingin Anda lakukan , itu tidak akan membantu Anda. Misalnya, mungkin ingin tidak mengetahui apa tipe variabelnya, tetapi ingin mengetahui apakah tipe nilainya adalah tipe tertentu, seperti ini:
val x: Any = 5
def f[T](v: T) = v match {
case _: Int => "Int"
case _: String => "String"
case _ => "Unknown"
}
f(x)
Di sini tidak masalah apa jenis variabelnya Any
,. Yang penting, yang dicentang adalah jenisnya 5
, nilainya. Faktanya, T
itu tidak berguna - Anda mungkin juga yang menulisnya def f(v: Any)
. Juga, ini menggunakan salah satu ClassTag
atau nilai Class
, yang dijelaskan di bawah, dan tidak dapat memeriksa parameter tipe dari suatu tipe: Anda dapat memeriksa apakah sesuatu adalah List[_]
( List
dari sesuatu), tetapi tidak apakah itu, misalnya, List[Int]
atau List[String]
.
Kemungkinan lain adalah Anda ingin merefleksikan tipe variabel. Yaitu, Anda ingin mengubah jenis menjadi nilai, sehingga Anda dapat menyimpannya, menyebarkannya, dll. Ini melibatkan refleksi, dan Anda akan menggunakan salah satu ClassTag
atau a TypeTag
. Sebagai contoh:
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"
A ClassTag
juga akan membiarkan Anda menggunakan parameter tipe yang Anda terima match
. Ini tidak akan berhasil:
def f[A, B](a: A, b: B) = a match {
case _: B => "A is a B"
case _ => "A is not a B"
}
Tapi ini akan:
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)
Di sini saya menggunakan sintaks batas konteksB : ClassTag
, yang berfungsi seperti parameter implisit dalam ClassTag
contoh sebelumnya , tetapi menggunakan variabel anonim.
Anda juga bisa mendapatkan ClassTag
nilai Class
, seperti ini:
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
dibatasi karena hanya mencakup kelas dasar, tetapi tidak jenis parameternya. Artinya, ClassTag
untuk List[Int]
dan List[String]
adalah sama List
,. Jika Anda membutuhkan parameter tipe, maka Anda harus menggunakan a TypeTag
. TypeTag
Namun, A tidak dapat diperoleh dari suatu nilai, juga tidak dapat digunakan pada pencocokan pola, karena penghapusan JVM .
Contoh dengan TypeTag
can menjadi sangat rumit - bahkan membandingkan dua jenis tag tidaklah sederhana, seperti yang dapat dilihat di bawah ini:
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
Tentu saja, ada cara untuk membuat perbandingan itu kembali menjadi kenyataan, tetapi itu akan membutuhkan beberapa bab buku untuk benar-benar dibahas TypeTag
, jadi saya akan berhenti di sini.
Terakhir, mungkin Anda sama sekali tidak peduli dengan jenis variabelnya. Mungkin Anda hanya ingin tahu apa itu kelas suatu nilai, dalam hal ini jawabannya agak sederhana:
val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it
Akan lebih baik, bagaimanapun, untuk lebih spesifik tentang apa yang ingin Anda capai, sehingga jawabannya bisa lebih tepat sasaran.
Int
adalah Any
, tetapi Any
tidak Int
. Ia bekerja pada Scala 2.10, dan seharusnya bekerja pada Scala 2.11, dan saya tidak tahu mengapa tidak.
a match { case _: B => ...
menguji jenis nilai aktual variabel a
, bukan jenis variabel a
. Anda benar karena ia mengembalikan apa yang Anda katakan dalam skala 2.10.6. Tapi itu pasti bug. Dalam skala 2.11.8, jenis nilai aktual diuji sebagaimana mestinya.
Saya pikir pertanyaannya tidak lengkap. jika Anda bermaksud bahwa Anda ingin mendapatkan informasi tipe dari beberapa kelas tipe maka di bawah ini:
Jika Anda ingin mencetak seperti yang Anda tentukan maka:
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]
Jika Anda dalam mode repl maka
scala> :type List(1,2,3)
List[Int]
Atau jika Anda hanya ingin tahu apa jenis kelasnya maka seperti yang dijelaskan @monkjack "string".getClass
mungkin bisa menyelesaikan tujuan tersebut
typeof x
, di sini manOf(x)
ucapkan tipe datanya!
Jika jenis variabel yang Anda maksud adalah kelas runtime dari objek yang ditunjuk variabel, maka Anda bisa mendapatkan ini melalui referensi kelas yang dimiliki semua objek.
val name = "sam";
name: java.lang.String = sam
name.getClass
res0: java.lang.Class[_] = class java.lang.String
Namun jika yang Anda maksud adalah tipe variabel itu dideklarasikan sebagai, maka Anda tidak bisa mendapatkannya. Misalnya, jika Anda mengatakan
val name: Object = "sam"
maka Anda masih akan mendapatkan String
kembali dari kode di atas.
name.getClass.getSimpleName
untuk hasil yang lebih mudah dibaca
Saya telah mengujinya dan berhasil
val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
5
adalah turunan dariInt
dan turunan dariAny
. Selain itu, penjelasan Anda sempurna :)