Scala Case 类与别名内部类型匹配编译错误?

发布于 2024-08-13 00:16:18 字数 541 浏览 7 评论 0原文

如何使用与别名类型匹配的案例类?当我从容器中取出 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
}

谢谢!

How do I use case class matching with aliased types? This works when I pull CB etc out of Container.

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
}

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

疯了 2024-08-20 00:16:18

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

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

现在,考虑以下代码片段:

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 是一个实用方法,定义为这样的。 (它只是通过相当好的类型推断和一些 Manifest 的使用来给出其参数的类型)

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

当内部对象保存对其封闭对象的引用时,您不能 实例化一个内部对象而不以某种方式指定其外部对象。因此,您可以调用 new x.Bar("one") 但不能调用 new Foo#Bar("?") - 就像在第二种情况下您没有调用t 指定您尝试构造的新对象的内部对象是什么。

那么,让我们回到您的代码片段。当您进行模式匹配时,您实际上是在调用构造函数 - 当调用 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 类型,其中 cd 中的 CB 恰好是相同的。

考虑这个例子:

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
  }
}

希望它有帮助:)

-- Flaviu Cipcigan

Right... Inner classes in Scala are a bit fiddly. Let's try a simple example before I show you the rewritten version of the code you have provided.

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

Now, consider the following code snippet:

val x = new Foo(1)
val y = new Foo(2)

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

The most generic type of a and b is Foo#Bar, which means the inner class Bar with any outer object of type Foo. But we could be more specific in saying that the type of a is x.Bar and the type of b is y.Bar - which means that a is an instance of the inner class Bar with the outer object x, similar for b.

You can actually see that the types are different by calling typeOf(a) and typeOf(b), where typeOf is a utility method defined as such. (it just gives the type of its argument by quite nice type inference and a bit of use of Manifests)

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

As an inner object holds a reference to its enclosing object, you cannot instantiate an inner object without somehow specifying its outer object. Therefore, you can call new x.Bar("one") but you cannot call new Foo#Bar("?") - as in the second case you haven't specified what is the inner object for the new object you try to construct.

So, let's return to your code snippet. When you are pattern matching, you are actually calling a constructor - when calling C1(e1). As C1 is an alias for Container[TKey]#C1
you have tried to call a constructor of an inner class without specifying its outer object, which fails due to the reasons outlined above. The way I would write the code would be as follows:

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
  }
}

Now this compiles and hopefully it does what you want. But take this with great care! Due to type erasure, Scala cannot guarantee that the element is actually of type c.CB or of type d.CB where the CB in the case of c and d happen to be the same.

Consider this example:

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

where x and y are as before. Try running the following:

matcher(a)
matcher(b) 

They both print x!

Therefore I would rewrite the code to explicitly have an element in the container:

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
  }
}

Hope it helps :)

-- Flaviu Cipcigan

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文