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


Відповіді:


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.