首页 > 解决方案 > 为什么Scala PartialFunction isDefinedAt 方法总是返回true?

问题描述

我的代码如下。

    case class C[T]() {
      val pf:PartialFunction[Any,Any] = {
        case i:T => i
      }
    }

    println(C[Int]().pf.isDefinedAt(-1.0))

这打印true。为什么会发生这种情况?

标签: scalapartialfunction

解决方案


由于类型擦除,您的代码基本上等于:

case class C[T]() {
  val pf:PartialFunction[Any,Any] = {
    case i:Any => i // this matches everything
  }
}

您可以使用TypeTags 来修复它:

import scala.reflect.runtime.universe._
case class C[T: TypeTag]() {
  def pf[U: TypeTag]: PartialFunction[U, Any] = {
    case i if typeOf[U] <:< typeOf[T] => i
  }
}

正在使用:

@ C[Int]().pf.isDefinedAt("")
res41: Boolean = false

@ C[Int]().pf.isDefinedAt(34)
res42: Boolean = true

这些实际上等于

@ C[Int]().pf[String].isDefinedAt("")
res41: Boolean = false

@ C[Int]().pf[Int].isDefinedAt(34)
res42: Boolean = true

推断类型U的地方 - 它有一个限制,即它只能在需要时与编译器对类型的了解一样精确TypeTag

您也可以尝试ClassTag[T]使用运行时反射......但对于原语它会失败

case class C[T]()(implicit classTag: scala.reflect.ClassTag[T]) {
  def pf[U: TypeTag]: PartialFunction[U, Any] = {
    case i if classTag.runtimeClass.isInstance(i) => i
  }
}

这导致

@ C[Int]().pf.isDefinedAt(34)
res2: Boolean = false

@ C[Int]().pf.isDefinedAt("")
res3: Boolean = false

事情classTag会解决 Scala 的int,而运行时会显示java.lang.Int

@ case class C[T]()(implicit classTag: scala.reflect.ClassTag[T]) {
    def pf: PartialFunction[Any, Any] = {
      case i => println(s"T = ${classTag.runtimeClass.getName}, here: ${i.getClass.getName}")
    }
  }
defined class C

@ C[Int]().pf.isDefinedAt(34)
res7: Boolean = true

@ C[Int]().pf(34)
T = int, here: java.lang.Integer
res8: Any = ()

一般来说,这里没有完美的解决方案,您可以在此处此处阅读有关类似问题的更多信息。


推荐阅读