【问题标题】:Implicit ClassTag in pattern matching模式匹配中的隐式 ClassTag
【发布时间】:2016-04-27 04:33:04
【问题描述】:

我们以这个为例:

import scala.reflect._

def get[T](list: List[Any])(implicit tag: ClassTag[T]) = {
  list.flatMap {
    case element: T => Some(element)
    case _ => None
  }
}

我可以使用 get() 从列表中获取 T 类型的值(例如,get[String](list) 将提供该列表中的所有字符串)。

现在,我了解到编译器会自动提供 ClassTag[String] 类型的值。我也知道ClassTag 是一个类型类,在幕后某处有一段代码写着implicitly[ClassTag[T]].getRuntimeClass() 或其他什么。

但如果是这样,我们怎么能在没有类标签的情况下进行模式匹配(在这种情况下我们无法区分擦除的类型)?我的意思是,它是如何实现的,如果我声明一个隐式参数(由编译器自动提供),我会得到一种行为,但如果我没有得到不同的行为?

【问题讨论】:

  • 顺便说一句,你可以像def get[T : ClassTag](list: List[Any]) = list collect { case element: T => element }这样让这段代码更简洁
  • 是的,我知道,这样更明显的是tag 没有在方法中使用。似乎有点神奇的是,有可能添加一个隐式参数而不用它显式地做任何事情,但却得到与参数不存在时不同的行为。
  • 是的,我明白你的问题,我自己也觉得它很有趣,我投了赞成票,我正在等待答案,但我猜编译器在这里做了一些事情。
  • Documentation 只是声明,如果你声明一个类型为 ClassTag 的隐式参数((Weak)TypeTags 也是如此)编译器将为你提供一个。但是,一旦您进入该方法,并且可能有也可能没有 ClassTag 类型的隐式值可供使用,您将不会知道发生了什么。

标签: scala reflection


【解决方案1】:

编译器会自动将你的代码大致翻译成这样:

  def get[T](list: List[Any])(implicit tag: ClassTag[T]) = list.flatMap {
    case (element @ tag(_: T)) => Some(element)
    case _ => None
  }

ClassTag 有一个unapply(x: Any) 重载,允许它对值进行模式匹配。我已经清理了从reify 获得的树,只显示相关部分,但这会显示完整的树:

scala.reflect.runtime.universe.reify { 
    def get[T](list: List[Any])(implicit tag: ClassTag[T]) = {
      list.flatMap {
        case element: T => Some(element)
        case _ => None
      }
    }
}

另见scaladoc

编译器尝试通过将(_: T) 类型模式包装为ct(_: T) 来将模式匹配中的未检查类型测试转换为已检查类型测试,其中ctClassTag[T] 实例。在调用其他提取器之前进行必要的类型测试的处理方式类似。如果SomeExtractor.unapply(x: T) 中的T 是不可检查的,则SomeExtractor(...) 会变成ct(SomeExtractor(...)),但我们有一个ClassTag[T] 的实例。

【讨论】:

  • 如果我没有隐式参数?
  • 那么它没有ClassTag 可以用作提取器,所以它会遭受T 上的类型擦除。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-10
  • 2021-02-05
  • 2022-01-11
相关资源
最近更新 更多