【问题标题】:Scala type erasure for pattern matching用于模式匹配的 Scala 类型擦除
【发布时间】:2012-08-08 12:25:10
【问题描述】:

我一直在论坛和 Google 上搜索有关 Scala 类型擦除问题的答案。但是,我找不到任何可以回答我的问题的内容。

我在与 ParamClass 的类型参数匹配的对象上进行模式匹配。我需要对 bar 方法的传入对象类型进行模式匹配。我见过诸如

之类的解决方案
bar[X](a : X)(implicit m : Manifest[X])

这可以解决我的问题,但我不能使用它,因为 bar 方法是一种被覆盖的方法。 (其实就是Akka actor框架中的receive偏函数)。代码如下,应该是不言自明的:

class ParamClass[A : Manifest] {
  def bar(x : Any) = x match {
    case a: A => println("Found A: " + a)
    case _ =>    println("No match: " + x)
  }
}

object ErasureIssue {
  def main(args: Array[String]) {
    val clz = new ParamClass[Int]
    clz.bar("faf")
    clz.bar(2.3)
    clz.bar(12)   // this should match, but does not
  }
}

ErasureIssue.main(null)

非常感谢您对解决此问题的任何帮助。我正在使用 Scala 2.9.1,顺便说一句。

-J

【问题讨论】:

    标签: scala pattern-matching type-erasure


    【解决方案1】:

    理论上你可以像这样签入barx.getClass == implicitly[Manifest[A]].erasure,但是对于诸如Int之类的原始类型,清单正确擦除到Int,但bar是用盒装类型调用的java.lang.Integer ... :-(

    您可以要求 A 成为 AnyRef 以获得盒装清单:

    class ParamClass[A <: AnyRef : Manifest] {
      def bar(x : Any) = x match {
        case _ if x.getClass == implicitly[Manifest[A]].erasure =>
          println("Found A: " + x.asInstanceOf[A])
        case _ => println("No match: " + x)
      }
    }
    
    object ErasureIssue {
      def main(args: Array[String]) {
        val clz = new ParamClass[Integer] // not pretty...
        clz.bar("faf")
        clz.bar(2.3)
        clz.bar(12)   // ok
      }
    }
    
    ErasureIssue.main(null)
    

    鉴于您对构造原始数组的要求,您可以直接存储已装箱的类,而与未装箱的清单无关:

    object ParamClass {
      def apply[A](implicit mf: Manifest[A]) = {
        val clazz = mf match {
          case Manifest.Int => classOf[java.lang.Integer] // boxed!
          case Manifest.Boolean => classOf[java.lang.Boolean]
          case _ => mf.erasure
        }
        new ParamClass[A](clazz)
      }
    }
    class ParamClass[A] private[ParamClass](clazz: Class[_])(implicit mf: Manifest[A]) {
      def bar(x : Any) = x match {
        case _ if x.getClass == clazz =>
          println("Found A: " + x.asInstanceOf[A])
        case _ => println("No match: " + x)
      }
    
      def newArray(size: Int) = new Array[A](size)
    
      override def toString = "ParamClass[" + mf + "]"
    }
    
    val pi = ParamClass[Int]
    pi.bar("faf")
    pi.bar(12)
    pi.newArray(4)
    
    val ps = ParamClass[String]
    ps.bar("faf")
    ps.bar(12)
    ps.newArray(4)
    

    【讨论】:

    • 感谢您的回答(它会 +1,但我的代表还不够高)。我不知道在 A 的清单上“隐式”执行。我也许可以使用它,但是,在我的一个问题中,出于性能原因,类型参数只是原始类型。在这种情况下我该怎么办?
    • 请注意,使用def bar(x: Any),您必须有拳击,所以除非您有很多其他使用A 类型的方法并且您在ParamClass 定义上使用@specialized,否则您赢了'对于原始类型没有任何性能优势。
    • A 参数在内部用于创建一个 Array[A] ,大多数代码都在该 Array[A] 上运行(期望例如 int[] 内部)。在这种情况下,bar 方法仅在非常偶然的情况下使用。但是,我在创建 ParamClass 时没有使用 @specialized 表示法,所以在使用 @specialized 之前我可能会无缘无故地跳槽?!
    【解决方案2】:

    如果您尝试使用 -unchecked 进行编译,您会立即收到警告。

    test.scala:3: 警告:类型模式 A 中的抽象类型 A 未选中 因为它被擦除消除了 case a: A => println("找到 A:" + a)

    如果您现在想更深入,可以使用 scalac -print

    [[syntax trees at end of cleanup]]// Scala source: test.scala
    package <empty> {
      class ParamClass extends java.lang.Object with ScalaObject {
        def bar(x: java.lang.Object): Unit = {
          <synthetic> val temp1: java.lang.Object = x;
          if (temp1.$isInstanceOf[java.lang.Object]())
            {
              scala.this.Predef.println("Found A: ".+(temp1))
            }
          else
            {
              scala.this.Predef.println("No match: ".+(x))
            }
        };
        def this(implicit evidence$1: scala.reflect.Manifest): ParamClass = {
          ParamClass.super.this();
          ()
        }
      };
      final object ErasureIssue extends java.lang.Object with ScalaObject {
        def main(args: Array[java.lang.String]): Unit = {
          val clz: ParamClass = new ParamClass(reflect.this.Manifest.Int());
          clz.bar("faf");
          clz.bar(scala.Double.box(2.3));
          clz.bar(scala.Int.box(12))
        };
        def this(): object ErasureIssue = {
          ErasureIssue.super.this();
          ()
        }
      }
    }
    

    现在看到这段代码你可以看到你的A变成了java.lang.Object,导致所有参数都匹配子句

    【讨论】:

      猜你喜欢
      • 2018-04-12
      • 2015-10-06
      • 2016-01-24
      • 2017-11-23
      • 2015-08-18
      • 2013-09-07
      • 1970-01-01
      • 2015-11-21
      • 1970-01-01
      相关资源
      最近更新 更多