Bagaimana menemukan tipe data parameter kelas saat runtime dalam skala

Nov 29 2020
import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._

def getType[T: TypeTag](obj: T) = typeOf[T]

case class Thing(
  val id: Int,
  var name: String
)
val thing = Thing(1, "Apple")

val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature

dataType match {
 case t if t =:= typeOf[Int] => println("I am Int")
 case t if t =:= typeOf[String] => println("String, Do some stuff")
 case _ => println("Absurd")
}

Tidak bisa mencerna mengapa hasilnya Absurdbukan I am int?

Tujuan saya adalah untuk mengetahui jenis data parameter kelas pada waktu proses dan mencocokkannya dengan jenis yang telah ditentukan.

Jawaban

4 DmytroMitin Nov 29 2020 at 17:20

Keduanya terkini dataTypedan typeOf[Int]dicetak seolah- Intolah tetapi jika Anda melakukannya showRawAnda akan melihat mengapa mereka tidak cocok

showRaw(dataType) // NullaryMethodType(TypeRef(ThisType(scala), scala.Int, List()))
showRaw(typeOf[Int]) // TypeRef(ThisType(scala), scala.Int, List())

Masalahnya adalah hanya tipe Intdan tipe metode nullary yang dikembalikan Intadalah tipe yang berbeda.

Coba tambahkan .resultType

val dataType = getType(thing).decl(TermName("id")).asTerm.typeSignature.resultType

dataType match {
  case t if t =:= typeOf[Int] => println("I am Int")
  case t if t =:= typeOf[String] => println("String, Do some stuff")
  case _ => println("Absurd")
} // I am Int

Perlu juga disebutkan bahwa .decl(TermName("id"))mengembalikan simbol pengambil, itu .decl(TermName("id "))(dengan spasi kosong) yang mengembalikan simbol bidang. Jadi sebagai alternatif Anda dapat melakukannya dengan spasi kosong di nama simbol dan tanpa.resultType

val dataType = getType(thing).decl(TermName("id ")).asTerm.typeSignature

Aku akan menambah @TomerShetah 's jawaban bahwa jika tujuannya adalah 'pola pencocokan' semua bidang kelas kasus maka ini dapat dilakukan juga pada waktu kompilasi (kebanyakan) dengan tak berbentuk :

import shapeless.Poly1
import shapeless.syntax.std.product._

object printTypes extends Poly1 {
  implicit val int: Case.Aux[Int, Unit] = at(t => println(s"I am Int: $t")) implicit val string: Case.Aux[String, Unit] = at(t => println(s"String, Do some stuff: $t"))
  implicit def default[V]: Case.Aux[V, Unit] = at(t => println(s"Absurd: $t"))
}
  
thing.toHList.map(printTypes)
// I am Int: 1
// String, Do some stuff: Apple

https://scastie.scala-lang.org/DmytroMitin/N4Idk4KcRumQJZE2CHC0yQ

1 TomerShetah Nov 30 2020 at 11:28

Jawaban @Dmytrio adalah penjelasan yang bagus mengapa refleksi tidak berfungsi seperti yang Anda harapkan.

Saya dapat memahami dari pertanyaan Anda, bahwa apa yang Anda coba lakukan, sebenarnya adalah pola yang cocok dengan semua variabel yang Anda miliki di kelas kasus. Harap pertimbangkan untuk melakukannya dengan cara berikut:

case class Thing(id: Int, name: String)
val thing = Thing(1, "Apple")

thing.productIterator.foreach {
  case t: Int => println(s"I am Int: $t") case t: String => println(s"String, Do some stuff: $t")
  case t => println(s"Absurd: $t")
} 

Kode dijalankan di Scastie .