Çalışma zamanında bir değişkenin türünü almak istiyorum


Yanıtlar:


133

Yani, kesin olarak söylemek gerekirse, "değişken türü" her zaman mevcuttur ve bir tür parametresi olarak aktarılabilir. Örneğin:

val x = 5
def f[T](v: T) = v
f(x) // T is Int, the type of x

Ancak ne yapmak istediğinize bağlı olarak , bu size yardımcı olmayacaktır. Örneğin, değişkenin türünün ne olduğunu bilmek istemeyebilir, ancak değerin türünün aşağıdaki gibi belirli bir tür olup olmadığını bilmek isteyebilirsiniz :

val x: Any = 5
def f[T](v: T) = v match {
  case _: Int    => "Int"
  case _: String => "String"
  case _         => "Unknown"
}
f(x)

Burada değişkenin türünün ne olduğu önemli değil Any. Önemli olan, kontrol edilen şeyin türü 5, değeridir. Aslında Tişe yaramaz - onun def f(v: Any)yerine yazmış olabilirsin . Ayrıca, bu aşağıda açıklanan ClassTagdeğerlerden birini veya değerini kullanır Classve bir türün tip parametrelerini kontrol edemez: bir şeyin List[_]( Listbir şeyin) olup olmadığını kontrol edebilirsiniz , ancak örneğin a List[Int]veya olup olmadığını kontrol edemezsiniz List[String].

Diğer bir olasılık, değişkenin türünü yeniden belirtmek istemenizdir . Yani, türü bir değere dönüştürmek istersiniz, böylece onu saklayabilir, başka bir yere aktarabilirsiniz. Bu yansıma içerir ve ya ya ClassTagda a kullanacaksınız TypeTag. Örneğin:

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 ClassTagayrıca aldığınız tür parametrelerini kullanmanıza izin verir match. Bu işe yaramaz:

def f[A, B](a: A, b: B) = a match {
  case _: B => "A is a B"
  case _ => "A is not a B"
}

Ancak bu:

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)

Burada , önceki örnekteki örtük parametre gibi çalışan , ancak anonim bir değişken kullanan bağlam sınırları sözdizimini kullanıyorum.B : ClassTagClassTag

Bir de ClassTagbir değerden şu şekilde bir alabilir 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, yalnızca temel sınıfı kapsaması, ancak tür parametrelerini kapsamaması açısından sınırlıdır. Yani, ClassTagfor List[Int]ve List[String]aynıdır List. Tür parametrelerine ihtiyacınız varsa, TypeTagbunun yerine a kullanmanız gerekir . Bir TypeTagnedeniyle JVM, ancak, bir değerden elde edilemez, ne de bir desen maç kullanılabilir silinmeye .

Örnekler TypeTagoldukça karmaşık olabilir - iki tip etiketi karşılaştırmak bile aşağıda görülebileceği gibi tam olarak basit değildir:

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

Elbette, bu karşılaştırmayı gerçeğe dönüştürmenin yolları var, ancak gerçekten ele almak için birkaç kitap bölümü gerektirecek TypeTag, bu yüzden burada duracağım.

Son olarak, belki de değişkenin türünü hiç umursamıyorsunuzdur. Belki sadece bir değerin sınıfının ne olduğunu bilmek istersiniz, bu durumda cevap oldukça basittir:

val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it

Bununla birlikte, neyi başarmak istediğiniz konusunda daha net olmanız daha iyi olur, böylece cevap daha doğru olabilir.


"Ama bu olacak:" dan sonra yazdığınız örnek kod kafa karıştırıcı. Derler ancak sonuç, yorumlarda gösterdiğiniz sonuç değildir. Her iki çağrı da aynı sonucu döndürür: "A, B'dir". Çünkü değer 5hem örneği hem de Intörneğidir Any. Bunun dışında açıklamanız mükemmeldi :)
Readren

@Readren Değer için test edilmez, sınıftır. Intolduğunu Any, ancak Anydeğildir Int. Scala 2.10 üzerinde çalışıyor ve Scala 2.11 üzerinde çalışıyor olmalı ve neden olmadığını bilmiyorum.
Daniel C. Sobral

1
Senin gibi bir üstünlükle çelişmek beni korkutuyor, ancak kod a match { case _: B => ...değişkenin atürünü değil, değişkenin gerçek değerinin türünü test ediyor a. Ölçek 2.10.6'da söylediklerinizi döndürdüğü konusunda haklısınız. Ama bir hata olmalı. Ölçek 2.11.8'de, olması gerektiği gibi gerçek değerin türü test edilir.
Readren

ClassTag ve TypeTag arasındaki farklar hakkında çok güzel bir haber, tam da aradığım şey.
marcin_koss

Bunu kontrol etmenin bir yolu var mı?
ChiMo

53

Bence soru eksik. Bazı tip sınıflarının tür bilgilerini almak istediğinizi kastetmişseniz, aşağıdan:

Belirttiğiniz gibi yazdırmak isterseniz, o zaman:

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]

Eğer repl modundaysanız o zaman

scala> :type List(1,2,3)
List[Int]

Veya sadece sınıfın türünü öğrenmek istiyorsanız @monkjack'in açıkladığı gibi "string".getClassamacı çözebilir


3
okuyucular için: bu en kullanışlı çözümdür . Javascript'te olduğu gibi typeof x, burada manOf(x)veri türünü söyleyin!
Peter Krauss

23

Tarafından Eğer bir değişkenin türü nesnenin çalışma zamanı sınıfını anlamında değişken puan, o zaman tüm nesneler olduğunu sınıf referansı ile bu alabilirsiniz.

val name = "sam";
name: java.lang.String = sam
name.getClass
res0: java.lang.Class[_] = class java.lang.String

Bununla birlikte, değişkenin bildirildiği türü kastediyorsanız, bunu anlayamazsınız. Örneğin, diyorsan

val name: Object = "sam"

o zaman Stringyukarıdaki koddan yine de bir geri alacaksınız .


8
name.getClass.getSimpleNameDaha okunaklı bir çıktı için de yapabilirsiniz
David Arenburg

21

bunu test ettim ve işe yaradı

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.