Я хочу отримати тип змінної під час виконання


Відповіді:


132

Отже, строго кажучи, "тип змінної" присутній завжди і може передаватися як параметр типу. Наприклад:

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або a 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"

Також A 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прикладі, але використовує анонімну змінну.

Також можна отримати 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обмежений тим, що охоплює лише базовий клас, але не параметри його типу. Тобто, ClassTagfor 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

Однак було б краще визначитися конкретніше з тим, що ви хочете досягти, щоб відповідь була більш конкретною.


Приклад коду, який ви написали після "Але це буде:", бентежить. Він компілюється, але результат не той, який ви показуєте в коментарях. Обидва виклики повертають однаковий результат: "A - це B". Оскільки значення 5є і екземпляром, Intі екземпляром Any. Окрім цього, ваше пояснення було чудовим :)
Редрен

@Readren Значення не перевірено, клас є. Intє Any, але Anyні Int. Він працює на Scala 2.10, і він повинен працювати на Scala 2.11, і я не знаю, чому це не так.
Деніел К. Собрал

1
Мене боїться суперечити такому величності, як ви, але код a match { case _: B => ...перевіряє тип фактичного значення змінної a, а не тип змінної a. Ви маєте рацію, повертаючи те, що ви говорите в масштабі 2.10.6. Але це повинна бути помилка. У шкалі 2.11.8 тип фактичного значення перевіряється, як і слід.
Редрен

Дуже приємне висвітлення відмінностей між ClassTag та TypeTag, саме те, що я шукав.
marcin_koss

Чи є спосіб перевірити це на нуль?
ChiMo

53

Я думаю, що питання неповне. якщо ви мали на увазі, що хочете отримати інформацію про тип якогось класу типів, то нижче:

Якщо ви хочете надрукувати, як ви вказали тоді:

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може вирішити ціль


3
для читачів: це найкорисніше рішення . Як і в Javascript typeof x, тут manOf(x)говорять про тип даних!
Пітер Краус,

23

Якщо під типом змінної ви маєте на увазі клас середовища виконання об'єкта, на який вказує змінна, то ви можете отримати це за допомогою посилання на клас, яке є у всіх об'єктів.

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

Якщо ви, однак, маєте на увазі тип, яким була оголошена змінна, тоді ви не можете цього отримати. Наприклад, якщо ви скажете

val name: Object = "sam"

тоді ви все одно отримаєте Stringвідступ від наведеного вище коду.


8
Ви також можете зробити name.getClass.getSimpleNameдля більш читабельного результату
Девід Аренбург

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.