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

时间:2014-12-07 13:39:11

标签: scala comparison

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

case class User(name:Option[String])

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

if(user.name == "Freewind") { // never be true!!!
    ...
}

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

3 个答案:

答案 0 :(得分:4)

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

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)

这里变量的类型是什么并不重要。重要的是,检查的是5的类型,即值。事实上,T是没用的 - 你可能也会把它写成def f(v:Any)。此外,它使用ClassTag或值的类,这将在下面解释,并且无法检查类型的类型参数:您可以检查某些内容是否为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"

ClassTag还允许您使用匹配时收到的类型参数。这不起作用:

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

但这会:

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)

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

还可以从值的类中获取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

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

当然,有一些方法可以让比较返回true,但是需要一些书章才能真正涵盖TypeTag,所以我会在这里停下来。

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

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

然而,更好地了解您想要完成的任务,以便答案可以更加重要。

答案 1 :(得分:1)

实际上你不能用==运算符那样做,因为它就像Java Object.equals方法一样。 Scalaz实际上是通过构造新的运算符===解决了这个问题。 看看:scalaz.syntax.EqualOps

答案 2 :(得分:1)

Scalaz有一个Equal类型类(examples on Learning Scalaz)。另外我注意到有时scalac在比较不相关的类型时会发出警告,但似乎buggy