如何比较两个对象并要求它们具有相同的类型?

Fre*_*ind 4 comparison scala

我认为比较两个不同类型的对象是一个非常常见的错误,例如

case class User(name:Option[String])

val user = User(Some("Freewind"))

if(user.name == "Freewind") { // never be true!!!
    ...
}
Run Code Online (Sandbox Code Playgroud)

是否有任何方法可以比较两个对象,同时要求它们具有相同的类型?

Muh*_*raz 5

因此,严格来说,“变量的类型”始终存在,并且可以作为类型参数传递。例如:

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

但取决于你想做什么,这对你没有帮助。例如,可能不想知道变量的类型是什么,而是想知道值的类型是否是某种特定类型,例如:

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

这里变量的类型是什么并不重要,Any。重要的是,检查的是 5 的类型,即值。事实上,T 是无用的——您不妨将其写为 def f(v: Any)。另外,这使用 ClassTag 或值的 Class(如下所述),并且无法检查类型的类型参数:您可以检查某物是否是 List[_] (某物的列表),但不能检查它是否是,例如例如,List[Int] 或 List[String]。

另一种可能性是您想要具体化变量的类型。也就是说,您希望将类型转换为值,以便可以存储它、传递它等。这涉及反射,您将使用 ClassTag 或 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"
Run Code Online (Sandbox Code Playgroud)

ClassTag 还允许您使用在比赛中收到的类型参数。这是行不通的:

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

但这会:

val x: Any = 5
val y = 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 (Any) is not a B (Int)
f(y, x) // A (Int) is a B (Any)
Run Code Online (Sandbox Code Playgroud)

在这里,我使用上下文边界语法 B : ClassTag,其工作方式与前面的 ClassTag 示例中的隐式参数类似,但使用匿名变量。

我们还可以从值的 Class 获取 ClassTag,如下所示:

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
Run Code Online (Sandbox Code Playgroud)

ClassTag 的局限性在于它仅覆盖基类,但不覆盖其类型参数。也就是说,List[Int] 和 List[String] 的 ClassTag 是相同的,List。如果您需要类型参数,则必须使用 TypeTag。然而,由于 JVM 的擦除,无法从值获取 TypeTag,也无法将其用于模式匹配。

使用 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
Run Code Online (Sandbox Code Playgroud)

当然,有很多方法可以使比较返回 true,但是需要几本书的章节才能真正涵盖 TypeTag,所以我就到此为止。

最后,也许您根本不关心变量的类型。也许您只是想知道值的类别是什么,在这种情况下答案相当简单:

val x = 5
x.getClass // int -- technically, an Int cannot be a class, but Scala fakes it
Run Code Online (Sandbox Code Playgroud)

然而,最好更具体地说明您想要完成的任务,以便答案更切题。