Câu trả lời:
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ế, T
nó 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 ClassTag
hoặ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[_]
( List
củ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 ClassTag
hoặ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 ClassTag
cũ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 ClassTag
ví 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 ClassTag
từ 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 ClassTag
bị 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à, ClassTag
cho List[Int]
và List[String]
giống nhau , List
. Nếu bạn cần tham số kiểu, thì bạn phải sử dụng TypeTag
thay thế. Một TypeTag
tuy 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 TypeTag
có 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 đề.
Int
là Any
, nhưng Any
khô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.
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.
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".getClass
có thể giải quyết được mục đích
typeof x
, ở đây manOf(x)
nói kiểu dữ liệu!
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 String
lại được từ mã trên.
name.getClass.getSimpleName
cho một đầu ra dễ đọc hơn
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())}
5
vừa là thể hiện củaInt
vừa là thể hiện củaAny
. Ngoài ra, lời giải thích của bạn thật hoàn hảo :)