实现具有特征、不一致的编译器行为的抽象方法?

发布于 2024-09-08 07:15:07 字数 781 浏览 4 评论 0原文

我有一个来自 Java 库的基类,我无法修改其代码。这个类 (A) 有一个空方法 (b),它应该被声明为抽象:

class A {
  def b { }
}

我在 Scala 中扩展这个类并重写该方法以使其抽象:

abstract class AA extends A {
  override def b
}

现在我在一个特征中实现这个方法:

trait B {
  def b { println("B") }
}

如果我用以下方法扩展 AA特征 B,我收到错误:重写类型 => 的 A 类中的方法 b单元; 类型 => 的特质 B 中的方法 b单元需要“override”修饰符:

class C extends AA with B {}

相反,如果代码是这样的,则所有内容都会编译而不会出现错误,这对我来说似乎有点矛盾:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

我正在运行 Scala 2.8.0RC3,并且对该语言完全陌生(3 天) 。另一个奇怪且相关的行为是,在使 b 抽象时不需要覆盖标签:

abstract class AA extends A {
  def b
}

I have a base class that comes from a Java library, whose code I cannot modify. This class (A) has an empty method (b) which should have been declared as abstract instead:

class A {
  def b { }
}

I extend this class in Scala and override the method to make it abstract:

abstract class AA extends A {
  override def b
}

Now I implement this method in a trait:

trait B {
  def b { println("B") }
}

If I extend AA with trait B, I get a error: overriding method b in class A of type => Unit;
method b in trait B of type => Unit needs `override' modifier:

class C extends AA with B {}

Instead, if the code had been like this, everything is compiled without errors, which seems a bit contradictory to me:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

I'm running Scala 2.8.0RC3, and completely new to the language (3 days). Another weird and related behaviour is that the override label is not necessary when making b abstract:

abstract class AA extends A {
  def b
}

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

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

发布评论

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

评论(3

初心未许 2024-09-15 07:15:07

为了尝试了解发生了什么,我尝试了以下操作:

scala> class A{
     |   def b{ }
     | }
defined class A

scala> abstract class AA extends A{
     |   override def b
     | }
defined class AA

scala> class AAA extends AA{
     |   def b = println("AAA")
     | }
<console>:8: error: overriding method b in class A of type => Unit;
 method b needs `override' modifier
         def b = println("AAA")
             ^

显然,问题的根源在于抽象类无法“释放”其超类中的方法,使抽象类的子类不需要包含“覆盖”修饰符。

To try to see what's going on, I tried this:

scala> class A{
     |   def b{ }
     | }
defined class A

scala> abstract class AA extends A{
     |   override def b
     | }
defined class AA

scala> class AAA extends AA{
     |   def b = println("AAA")
     | }
<console>:8: error: overriding method b in class A of type => Unit;
 method b needs `override' modifier
         def b = println("AAA")
             ^

Apparently, the source of the problem is that abstract classes can't "free up" methods in their superclass from the need for the abstract class's subclasses to include the 'override' modifier.

他不在意 2024-09-15 07:15:07

不确定这是否是正确的解决方案,但如果您的 trait B 扩展 A (并覆盖 b),那么一切都可以正常编译:

首先让我们定义 AAA 就像你在问题中呈现它们一样:

C:\Users\VonC>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class A {
     | def b { println("A b") }
     | }
defined class A

scala> new A
res5: A = A@153bedc4

scala> res5.b
A b

scala> abstract class AA extends A {
     | override def b
     | }
defined class AA

你做了什么:

scala> trait B {
     | override def b { println("B b") }
     | }
<console>:6: error: method b overrides nothing
       override def b { println("B b") }
                    ^

我对特征 B 尝试了什么(为了能够添加 'override'):

scala> trait B extends A {
     | override def b { println("B b") }
     | }
defined trait B

所以现在:

scala> class C extends AA with B {}
defined class C

scala> new C
res7: C = C@1497b7b1

scala> res7.b
B b

使用 Cb 调用正确的 b 重写方法


至于您明显的“不一致”,请参阅 Scala for Java 难民第 5 部分:特征和类型

首先,有一个令人讨厌的override关键字。我在有关基本 OOP 的文章中提到过,任何重写超类中方法的方法都必须使用 override 修饰符进行声明。当时,我将其比作强制使用 @Override 注释的语言,其主要目的是强制实施良好实践。

特征威力的真正关键在于编译器在继承类中处理它们的方式。
Traits 实际上是 mixins,而不是真正的父类
任何非抽象特征成员实际上都包含在继承类中,就像该类的物理部分一样。好吧,虽然不是物理上的,但你明白了。
这就好像编译器对非抽象成员执行剪切和粘贴并将它们插入到继承类中。这意味着继承路径不存在歧义,也就意味着不存在钻石问题。

因此,在第二个示例中不需要 override 关键字。

Not sure if this is the right solution, but if your trait B extends A (and override b), then everything compile fine:

First let's define A and AA like you present them in your question:

C:\Users\VonC>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.

scala> class A {
     | def b { println("A b") }
     | }
defined class A

scala> new A
res5: A = A@153bedc4

scala> res5.b
A b

scala> abstract class AA extends A {
     | override def b
     | }
defined class AA

What you did:

scala> trait B {
     | override def b { println("B b") }
     | }
<console>:6: error: method b overrides nothing
       override def b { println("B b") }
                    ^

What I tried with trait B (in order to be able to add the 'override'):

scala> trait B extends A {
     | override def b { println("B b") }
     | }
defined trait B

So now:

scala> class C extends AA with B {}
defined class C

scala> new C
res7: C = C@1497b7b1

scala> res7.b
B b

The right b overriden method is called with C.b


As for your apparent "inconsistency", see Scala for Java Refugees Part 5: Traits and Types:

To start with, there’s that ever-annoying override keyword. I mentioned back in the article on basic OOP that any method which overrides a method in a superclass must be declared with the override modifier. At the time, I likened it to the language mandating the use of the @Override annotation, with its primary purpose being to enforce the good practice.

The real key to the power of traits is the way in which the compiler treats them in an inheriting class.
Traits are actually mixins, not true parent classes.
Any non-abstract trait members are actually included in the inheriting class, as in physically part of the class. Well, not physically, but you get the picture.
It’s as if the compiler performs a cut-and-paste with the non-abstract members and inserts them into the inheriting class. This means that there’s no ambiguity in the inheritance path, meaning no diamond problem.

So no need for override keyword in your second example.

风柔一江水 2024-09-15 07:15:07

问题非常微妙。根据经验,您的类 AA 扩展了 A,应该与扩展 A 的特征混合在一起。

你做到了:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B {
  def b { println("B") }
}

因此,当你混合 AAB 方法时,b 被定义了两次。一次由A(未覆盖,因为B中的定义替换了AA中的覆盖),第二次由B ,编译器无法选择其中之一,因为这两个(名称相同但不相关)方法之间没有层次结构。如果你愿意的话,可以这样想:编译器“混合”了AAB的主体;如果他选择 AA 中的方法,那么它将是抽象的,如果他选择 B 中的方法(应该发生什么),因为它不是覆盖,你只能使用两种方法b

为了解决这个问题,您需要确保两个方法覆盖相同的方法,在这种情况下,编译器会理解您正在谈论相同的方法,并优先考虑最后一个混合的特征。

现在,要重写 B 中的方法 b,该类还必须从 A 继承。因此,执行此操作的规范方法是:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B extends A{
  def b { println("B") }
}

class C extends AA with B {}

编译得很好。

现在,当您这样做时:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

很明显,这两种方法是相同的,因此编译器知道他必须使用特征中的方法。

其他解决方案包括:

  1. B 覆盖 AA
  2. A 抽象中制作 b (但你不希望这样)

同样,这个问题非常微妙,但我希望我能说得更清楚一些。要更好地理解,请阅读 Scala 的 Stackable Trait Pattern

The problem is very subtle. As a rule of thumb, your class AA, which extends A, should be mixed with traits that also extend A.

You did:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B {
  def b { println("B") }
}

Hence when you mix AA and B method b is DEFINED twice. Once by A (Not overrided because the definition in B replaced the override in AA) and the second by B, the compiler can't chose one over the other because there is no hierarchy between the two (equaly named but unrelated) methods. If you want, think about it like this: The compiler "mixes" the bodies of AA and B; if he choses the method from AA it will be abstract, if he choses the method from B (What should happend), since it's not an override, your stuck with two methods b.

To solve this, you want to make sure that both methods override the same method, in which case the the compiler will understand you are talking about the SAME method and will give priority to the last trait mixed.

Now, to override the method b in B, that class has also to inherit from A. So the canonical way to do this would be:

class A {
  def b { }
}

abstract class AA extends A {
  override def b
}

trait B extends A{
  def b { println("B") }
}

class C extends AA with B {}

Which compiles just fine.

Now, when you do:

abstract class AA {
  def b
}

trait B {
  def b { println("B") }
}

class C extends AA with B {}

it's clear that both methods are the same so the compiler knows he has to use the method from the trait.

Other solutions include:

  1. Make B override AA
  2. Make b in A abstract (But you didn't want that)

Again, the problem is very subtle but I hope I made it a little clearer. To get a better understandig read Scala's Stackable Trait Pattern.

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