Tôi muốn nhận loại biến trong thời gian chạy


97

Tôi muốn nhận kiểu của một biến trong thời gian chạy. Làm thế nào để tôi làm điều này?

Câu trả lời:


132

Vì vậy, nói một cách chính xác, "kiểu của một biến" luôn tồn tại và có thể được truyền xung quanh dưới dạng một tham số kiểu. Ví dụ:

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

Nhưng tùy thuộc vào những gì bạn muốn làm , điều đó sẽ không giúp bạn. Ví dụ: có thể không muốn biết kiểu của biến là gì, nhưng để biết liệu kiểu của giá trị có phải là kiểu cụ thể nào đó hay không, chẳng hạn như sau:

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

Ở đây không quan trọng loại biến là gì Any,. Điều quan trọng, những gì được kiểm tra là loại 5, giá trị. Trên thực tế, Tnó là vô ích - bạn cũng có thể viết nó def f(v: Any). Ngoài ra, điều này sử dụng một trong hai ClassTaghoặc một giá trị Class, được giải thích bên dưới và không thể kiểm tra các tham số kiểu của một loại: bạn có thể kiểm tra xem một cái gì đó có phải là List[_]( Listcủa một cái gì đó) hay không, chẳng hạn như a List[Int]hoặcList[String] .

Một khả năng khác là bạn muốn cụ thể hóa kiểu của biến. Nghĩa là, bạn muốn chuyển đổi kiểu thành một giá trị, vì vậy bạn có thể lưu trữ nó, chuyển nó xung quanh, v.v. Điều này liên quan đến sự phản chiếu và bạn sẽ sử dụng một trong hai ClassTaghoặc một TypeTag. Ví dụ:

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 ClassTagcũng sẽ cho phép bạn sử dụng các tham số loại mà bạn nhận được match. Điều này sẽ không hoạt động:

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

Nhưng điều này sẽ:

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)

Ở đây tôi đang sử dụng cú pháp giới hạn ngữ cảnhB : ClassTag , hoạt động giống như tham số ngầm định trong ClassTagví dụ trước , nhưng sử dụng một biến ẩn danh.

Người ta cũng có thể nhận được một ClassTagtừ một giá trị Class, như thế này:

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 ClassTagbị hạn chế ở chỗ nó chỉ bao gồm lớp cơ sở, nhưng không bao gồm các tham số kiểu của nó. Đó là, ClassTagcho List[Int]List[String]giống nhau , List. Nếu bạn cần tham số kiểu, thì bạn phải sử dụng TypeTagthay thế. Một TypeTagtuy nhiên, không thể có được từ một giá trị, và cũng không nó có thể được sử dụng trên một mô hình phù hợp, do JVM của tẩy xoá .

Các ví dụ với TypeTagcó thể trở nên khá phức tạp - ngay cả việc so sánh hai thẻ loại cũng không đơn giản chính xác, như có thể thấy bên dưới:

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

Tất nhiên, có nhiều cách để làm cho sự so sánh đó trở thành sự thật, nhưng nó sẽ yêu cầu một vài chương sách để thực sự bao quát TypeTag , vì vậy tôi sẽ dừng lại ở đây.

Cuối cùng, có thể bạn không quan tâm đến kiểu của biến. Có thể bạn chỉ muốn biết lớp của một giá trị là gì, trong trường hợp đó, câu trả lời khá đơn giản:

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

Tuy nhiên, sẽ tốt hơn nếu bạn nên cụ thể hơn về những gì bạn muốn đạt được, để câu trả lời có thể đi sâu vào vấn đề.


Mã ví dụ bạn đã viết sau "But this will:" là khó hiểu. Nó biên dịch nhưng kết quả không như bạn hiển thị trong các bình luận. Cả hai lệnh đều trả về cùng một kết quả: "A là B". Vì giá trị 5vừa là thể hiện của Intvừa là thể hiện của Any. Ngoài ra, lời giải thích của bạn thật hoàn hảo :)
Readren

@Readren Giá trị không được kiểm tra cho lớp. IntAny, nhưng Anykhông phải Int. Nó hoạt động trên Scala 2.10 và nó sẽ hoạt động trên Scala 2.11, và tôi không biết tại sao nó không hoạt động.
Daniel C. Sobral

1
Tôi sợ khi mâu thuẫn với một người xuất chúng như bạn, nhưng đoạn mã a match { case _: B => ...kiểm tra loại giá trị thực của biến a, không phải loại của biến a. Bạn đúng ở chỗ nó trả về những gì bạn nói trong scala 2.10.6. Nhưng nó phải là một lỗi. Trong scala 2.11.8, loại giá trị thực được kiểm tra, như nó cần.
Readren

Báo cáo rất tốt về sự khác biệt giữa ClassTag và TypeTag, đúng như những gì tôi đang tìm kiếm.
marcin_koss

Có cách nào để kiểm tra null không?
ChiMo

53

Tôi nghĩ rằng câu hỏi là không đầy đủ. nếu ý bạn là bạn muốn nhận thông tin loại của một số typeclass thì hãy bên dưới:

Nếu bạn muốn in như bạn đã chỉ định thì:

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]

Nếu bạn đang ở chế độ repl thì

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

Hoặc nếu bạn chỉ muốn biết loại lớp thì như @monkjack giải thích "string".getClasscó thể giải quyết được mục đích


3
cho người đọc: đây là giải pháp hữu ích nhất . Như trong Javascript typeof x, ở đây manOf(x)nói kiểu dữ liệu!
Peter Krauss

23

Nếu theo kiểu của một biến, bạn có nghĩa là lớp thời gian chạy của đối tượng mà biến trỏ tới, thì bạn có thể nhận được điều này thông qua tham chiếu lớp mà tất cả các đối tượng có.

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

Tuy nhiên, nếu ý của bạn là kiểu mà biến được khai báo, thì bạn không thể hiểu được. Ví dụ, nếu bạn nói

val name: Object = "sam"

thì bạn vẫn sẽ nhận Stringlại được từ mã trên.


8
Bạn cũng có thể làm name.getClass.getSimpleNamecho một đầu ra dễ đọc hơn
David Arenburg

21

tôi đã thử nghiệm điều đó và nó hoạt động

val x = 9
def printType[T](x:T) :Unit = {println(x.getClass.toString())}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.