【问题标题】:Scala Case class matching compile error with aliased inner types?Scala Case类匹配编译错误与别名内部类型?
【发布时间】:2010-12-21 05:25:52
【问题描述】:

如何使用与别名类型匹配的案例类?这在我将 CB 等从容器中拉出时有效。

class DoStuff[TKey](
  val c : Container[TKey]#CB
)
{
  type CB = Container[TKey]#CB
  type C1 = Container[TKey]#C1
  type C2 = Container[TKey]#C2

  c match {
    case C1(e1) => e1  //   - not found: value e1   - not found: value C1
    case C2(e2) => e2 //    - not found: value e2   - not found: value C2
  }
}

trait Container[TKey]
{
    abstract trait CB
    case class C1(val e : AnyRef) extends CB
    case class C2(val e : AnyRef) extends CB
}

谢谢!

【问题讨论】:

    标签: scala types match


    【解决方案1】:

    对... Scala 中的内部类有点繁琐。在我向您展示您提供的代码的重写版本之前,让我们尝试一个简单的示例。

    case class Foo(x: Int) {
      case class Bar(y: String) 
    }
    

    现在,考虑以下代码 sn-p:

    val x = new Foo(1)
    val y = new Foo(2)
    
    val a = new x.Bar("one")
    val b = new y.Bar("two") 
    

    ab 最通用的类​​型是Foo#Bar,这意味着内部类BarFoo 类型的任何外部对象。但我们可以更具体的说a 的类型是x.Barb 的类型是y.Bar——这意味着a 是内部类Bar 的一个实例对象x,类似b

    您实际上可以通过调用typeOf(a)typeOf(b) 看到类型不同,其中typeOf 是这样定义的实用方法。 (它只是通过非常好的类型推断和Manifests 的一点使用来给出它的参数的类型)

    def typeOf[T](x: T)(implicit m: scala.reflect.Manifest[T]) = m.toString
    

    由于内部对象持有对其封闭对象的引用,您不能在不指定外部对象的情况下实例化内部对象。因此,您可以调用new x.Bar("one"),但不能调用new Foo#Bar("?") - 因为在第二种情况下,您没有指定您尝试构造的新对象的内部对象是什么。

    那么,让我们回到您的代码 sn-p。当您进行模式匹配时,您实际上是在调用构造函数 - 当调用 C1(e1) 时。由于C1Container[TKey]#C1 的别名,您试图调用内部类的构造函数而不指定其外部对象,但由于上述原因而失败。我编写代码的方式如下:

    trait Container[TKey] {
        abstract trait CB
        case class C1(val e : AnyRef) extends CB
        case class C2(val e : AnyRef) extends CB
    }
    
    class DoStuff[TKey] (val c: Container[TKey], val element: Container[TKey]#CB) {
      element match {
        case c.C1(e1) => Some(e1)
        case c.C2(e2) => Some(e2)
        case _        => None
      }
    }

    现在可以编译了,希望它能达到你想要的效果。但请务必小心!由于类型擦除,Scala 不能保证 element 实际上是 c.CB 类型或 d.CB 类型,而 CBcd 的情况下恰好是相同的。

    考虑这个例子:

    def matcher(arg: Foo#Bar) = {
      arg match {
        case x.Bar(n) => println("x");
        case y.Bar(n) => println("y");
      }
    }
    

    xy 和以前一样。尝试运行以下命令:

    matcher(a)
    matcher(b) 
    

    他们都打印x

    因此我会重写代码以在容器中显式地拥有一个元素:

    trait Container[TKey] {
        abstract trait CB
        case class C1(val e : AnyRef) extends CB
        case class C2(val e : AnyRef) extends CB
        val element: CB
    }
    
    class DoStuff[TKey](val c: Container[TKey]) {
      c.element match {
        case c.C1(e1) => Some(e1)
        case c.C2(e2) => Some(e2)
        case _        => None
      }
    }

    希望对你有帮助:)

    -- 弗拉维乌·西普西根

    【讨论】:

    • 不好玩。我才刚开始回答呢! :-)
    猜你喜欢
    • 2014-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多