如何匹配自定义通用类型?

时间:2016-01-29 15:09:29

标签: scala generics pattern-matching

我正在尝试使用模式匹配来检测基于this answer的我自己的自定义类型的泛型类型。

作者提供的代码按预期工作:

import scala.reflect.runtime.{universe => ru}

def matchList[A: ru.TypeTag](list: List[A]) = list match {
  case strlist: List[String @unchecked] if ru.typeOf[A] =:= ru.typeOf[String] => println("A list of strings!")
  case intlist: List[Int @unchecked] if ru.typeOf[A] =:= ru.typeOf[Int] => println("A list of ints!")
}

matchList(List("a", "b", "c"))
matchList(List(1,2,3))

正确显示:

A list of strings!
A list of ints!

现在基于此我尝试应用相同的模式来检测我的自定义类Foo的泛型类型。以下代码是copy-pased,但使用Foo代替List

import scala.reflect.runtime.{universe => ru}

class Foo[T](val data: T)

def matchFoo[A: ru.TypeTag](foo: Foo[A]) = {
  println("Is string = " + (ru.typeOf[A] =:= ru.typeOf[String]))
  println("Is int = " + (ru.typeOf[A] =:= ru.typeOf[Int]))
  foo match {
    case fooStr: Foo[String @unchecked] if ru.typeOf[A] =:= ru.typeOf[String] => println("Found String")
    case fooInt: Foo[Int @unchecked] if ru.typeOf[A] =:= ru.typeOf[Int] => println("Found Int")
  }
}

matchFoo(new Foo[String]("str"))
println("------------")
matchFoo(new Foo[Int](123))

只有这次它输出的不是我所期望的:

Is string = true
Is int = false
Found String
------------
Is string = false
Is int = true
Found String  // wth?

第二次来电matchFoo(new Foo[Int](123))如何显示Found String?正如你所看到的,我甚至明确地印出了匹配条件,它们没问题。

在线代码:http://goo.gl/b5Ta7h

修改

我通过将匹配条件提取到变量中来实现它:

def matchFoo[A: ru.TypeTag](foo: Foo[A]) = {
    val isString: Boolean = ru.typeOf[A] =:= ru.typeOf[String]
    val isInt: Boolean = ru.typeOf[A] =:= ru.typeOf[Int]
    println("Is string = " + isString)
    println("Is int = " + isInt)
    foo match {
      case fooStr: Foo[String @unchecked] if isString => println("Found String")
      case fooInt: Foo[Int @unchecked] if isInt => println("Found Int")
    }
}

在线代码:http://goo.gl/mLxYY2

但在我看来,原始版本也应该有效。我不认为我在这里缺少运算符优先级,因为将条件括在括号中也没有帮助。

这是Scala中的错误吗?我正在使用Scala SDK v. 2.11.5JDK v. 1.8.0_25。在线CodingGround使用Scala SDK v. 2.10.3

编辑2:

我在Scala的bugtracker中为此打开了一个问题。您可以为其投票here

1 个答案:

答案 0 :(得分:0)

这看起来非常类似于编译器中的错误,它无法解析正确的隐式(可能存在@unchecked?)。

case fooStr: Foo[String @unchecked] if ru.typeOf[A] =:= ru.typeOf[String] =>
   println(implicitly[TypeTag[String]]) // will print TypeTag[Int]

通过查看字节代码,编译器使用传递给方法的TypeTag($evidence)。

(有限的)解决方法可能是使用ru.definitions.IntTpe

case fooStr: Foo[Int @unchecked] if ru.typeOf[A] =:= ru.definitions.IntTpe =>
   println("That's an Int")