【问题标题】:What is the reason behind the following 'illegal inheritance' (Scala 2.9.2)?以下“非法继承”(Scala 2.9.2)背后的原因是什么?
【发布时间】:2012-06-28 22:24:09
【问题描述】:

更新:我用一个更小更精确的例子重新表述了我的问题。

假设,我们有以下定义:

class A
trait TraitForA extends A

class D[T]
trait TraitForD extends D[A]

然后我们可以创建一个像这样的对象:

scala> new D[A] with TraitForD
res0: D[A] with TraitForD = $anon$1@145d424

但是我们不能创建以下对象:

scala> new D[A with TraitForA] with TraitForD
<console>:12: error: illegal inheritance;
 anonymous class $anon inherits different type instances of class D:
D[A] and D[A with TraitForA]
             new D[A with TraitForA] with TraitForD
                 ^

当使用 self 类型而不是 extends 时,也会发生同样的事情:

scala> trait TraitForD2 { self: D[A] => }
defined trait TraitForD2

scala> new D[A with TraitForA] with TraitForD2
<console>:12: error: illegal inheritance;
 self-type D[A with TraitForA] with TraitForD2 does not conform to TraitForD2's
selftype TraitForD2 with D[A]
              new D[A with TraitForA] with TraitForD2
                                           ^

上面的创建(究竟)有什么问题?

我的猜测是,D[A with TraitForA] 不被视为类型 D[A]。在 D 类中使用协变注释 +T 时,这两个示例都有效。

有人可以解释示例失败的原因以及它们与+T 合作的原因吗?

额外问题:有没有办法让示例在没有 +T 的情况下运行?

【问题讨论】:

  • 我认为您的解释大部分是正确的。还要考虑到类型参数会被擦除,因此您不可能将相同的特征与两个不同的类型参数混合在一起。我不明白你的最后一句话(部分原因是它有语法问题),也许澄清一下,因为这最终似乎是你的问题。
  • @Sciss 谢谢你的信息。我重新表述了我的问题。
  • 好的,我去看看。我将trait TraitForA 更正为trait TraitForA extends A,因为我认为这就是你的意思。

标签: scala inheritance traits type-erasure type-parameter


【解决方案1】:

考虑

class C[T]{ def f(t: T): T = t }

C[A with Y] 表示它将有一个f,它只会占用A with Y,并且只会返回A with YC[A] 无法满足满足此请求的功能。所以两者是冲突的。

由于类型声明的成功并不取决于所涉及的任何类的方法的详细信息,Z1 with Z2 或任何其他组合C[A]C[A with Y] 的方式肯定是错误的。

【讨论】:

    【解决方案2】:

    要查看方差问题,让我们向“TraitForX”添加一些方法。为简单起见,我将这些特征称为A1D1,我还将使AD 特征再次为简单起见。我不知道你是怎么想出有趣的结构的——我不知道trait 甚至可以扩展class,这很奇怪!!

    trait A
    trait A1 extends A { def schoko = 33 }
    
    trait D[T] { def result: T }
    trait D1 extends D[A] {
       def result: A = new A {}
    }
    

    现在我们知道我们可以用A1 的实例做一些事情,而用A 的实例做不到。此外,D1 有一个针对result 的具体实现。

    您的案例仍然可以重现:

    new D[A] with D1    // works.
    
    new D[A with A1] {  // works.
       def result = new A with A1 {}
       def test = result.schoko
    }
    

    您可以看到test 可以调用方法result,并且由于D 使用A with A1 进行参数化(顺便说一下,与A1 相同),因此可以随后调用schoko

    因此,以下内容永远不会起作用:

    new D[A with A1] with D1 {
       def test = result.schoko
    }
    

    编译器会说value schoko is not a member of A,这只是意味着它拒绝接受D被参数化。 result是在D1中实现的,返回一个A的实例(它不知道schoko),所以这里有一个内在的冲突。


    现在当D 改为

    trait D[+T] { def result: T }
    

    (其余的都和上面一样),你只是在“推迟”问题:

    new D[A with A1] with D1
    

    编译器现在抱怨error: overriding method result in trait D of type =&gt; A with A1(您观察到这是有效的,因为您实际上并没有实现result)。

    所以方差注释允许你做一些你以前不能做的事情。如果您没有实现result,则完全有可能:

    trait D2 extends D[A]
    
    new D[A with A1] with D2 {
       def result = new A with A1 {}
    }
    

    【讨论】:

      猜你喜欢
      • 2011-06-20
      • 1970-01-01
      • 2018-08-31
      • 2020-01-26
      • 2016-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-01
      相关资源
      最近更新 更多