Saya ingin mendapatkan jenis variabel saat runtime


Jawaban:


132

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, Titu tidak berguna - Anda mungkin juga yang menulisnya def f(v: Any). Juga, ini menggunakan salah satu ClassTagatau nilai Class, yang dijelaskan di bawah, dan tidak dapat memeriksa parameter tipe dari suatu tipe: Anda dapat memeriksa apakah sesuatu adalah List[_]( Listdari 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 ClassTagatau 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 ClassTagjuga 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 ClassTagcontoh sebelumnya , tetapi menggunakan variabel anonim.

Anda juga bisa mendapatkan ClassTagnilai 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 ClassTagdibatasi karena hanya mencakup kelas dasar, tetapi tidak jenis parameternya. Artinya, ClassTaguntuk List[Int]dan List[String]adalah sama List,. Jika Anda membutuhkan parameter tipe, maka Anda harus menggunakan a TypeTag. TypeTagNamun, A tidak dapat diperoleh dari suatu nilai, juga tidak dapat digunakan pada pencocokan pola, karena penghapusan JVM .

Contoh dengan TypeTagcan 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.


Kode contoh yang Anda tulis setelah "But this will:" membingungkan. Ini mengkompilasi tetapi hasilnya bukan yang Anda tunjukkan di komentar. Kedua panggilan mengembalikan hasil yang sama: "A is a B". Karena nilainya 5adalah turunan dari Intdan turunan dari Any. Selain itu, penjelasan Anda sempurna :)
Readren

@Readren Nilai tidak diuji, kelasnya. Intadalah Any, tetapi Anytidak Int. Ia bekerja pada Scala 2.10, dan seharusnya bekerja pada Scala 2.11, dan saya tidak tahu mengapa tidak.
Daniel C. Sobral

1
Ini membuat saya takut untuk mengkontradiksikan keunggulan seperti Anda, tetapi kode 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.
Readren

Cakupan yang sangat bagus tentang perbedaan antara ClassTag dan TypeTag, persis seperti yang saya cari.
marcin_koss

Apakah ada cara untuk membatalkan cek ini?
ChiMo

53

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".getClassmungkin bisa menyelesaikan tujuan tersebut


3
untuk pembaca: ini adalah solusi yang paling berguna . Seperti di Javascript typeof x, di sini manOf(x)ucapkan tipe datanya!
Peter Krauss

23

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 Stringkembali dari kode di atas.


8
Anda juga dapat melakukannya name.getClass.getSimpleNameuntuk hasil yang lebih mudah dibaca
David Arenburg

21

Saya telah mengujinya dan berhasil

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.