Scala:继承接口成员初始化

时间:2012-02-08 11:49:26

标签: java scala inheritance interface override

我有以下代码,部分使用Java,部分使用Scala:

爪哇:

public interface ISubject {
  public void a()
  //...
}

public class CSubject implements ISubject {
  public void a() { /*...*/ }
  public void b() { /*...*/ }
  //...
}

public abstract class AbstractTest {
  ISubject subject;
  //...

  public CSubject generateParticularSubject() {
    return new CSubject();
  }
}

Scala的:

object Test extends AbstractTest {

  override val subject: CSubject = generateParticularSubject() /*1*/

  subject.b() /*2*/
}

问题是:如果标记为“1”的行与上面代码中的行相同,则编译器会抱怨overriding variable subject in class AbstractTest of type ISubject; value subject has incompatible type。如果我删除第1行中的类型注释: CSubject和关键字override val,则该错误消失,但另一个错误显示在第2行:value b is not a member of ISubject

我理解这些错误的含义以及编译器认为解决这些问题的方式。我不明白的是,如何获得与Java相同的行为,在实现类的接口成员的情况下,可以使用任何实现接口的类来初始化它。在Scala中执行此操作的方法是什么?

更新:在Scala中实现这样的构造是不是真的吗?我之所以这样做是因为AbstractTest包含处理主题ISubject方面的方法以及应该覆盖subject并将其定义为更窄类实例的子类,必须实现特定于该类的方法。同时,我希望AbstractTest继续处理所有关于subject的问题,并且可以在通过它的界面ISubject处理时进行处理。

4 个答案:

答案 0 :(得分:3)

您要在Test对象中尝试实现的目标尚不清楚。如果您只需要使用generateParticularSubject()方法的结果作为CSubject的实例,只需为变量使用不同的名称,即

object Test extends AbstractTest {
  subj = generateParticularSubject()
  subj.b()
}
  

我不明白的是,如何获得与Java相同的行为,在实现类的接口成员的情况下,可以使用任何实现接口的类来初始化它。

这里所指的是“协变返回类型”,自版本5以来在Java中确实支持(其中允许在子类中重写的方法返回更窄的类型)。但是,您在Scala代码中尝试执行的操作与协变返回类型无关,而是尝试覆盖基类的成员字段,这实际上并不是一个好主意。该字段在基类中声明为具有类型ISubject,并且应该保持不变。这就是为什么你可以写:

object Test extends AbstractTest {
  subject = generateParticularSubject()
  // ...
}

但只能调用ISubject中定义的方法(因为这是主题的静态类型,不管generateParticularSubject()返回的类型)

答案 1 :(得分:3)

Java字段subject是可变的,因此无法使用val在Scala中覆盖它。此外,由于可以分配,因此同时改变它是不安全的。

您可以通过将subject作为抽象方法来修复代码。正如您在Scala代码中所做的那样,这可以与val共同覆盖。

 abstract class AbstractTest {
   public abstract ISubject subject();

   //...

   public CSubject generateParticularSubject() {
     return new CSubject();
   }
 }

答案 2 :(得分:2)

我认为你有两种选择。

简单的一个:

object Test extends AbstractTest {

  val csubject: CSubject = generateParticularSubject() 
  override val subject = csubject

  csubject.b() 
}

更好的一个,特别是当你想要访问主题的各个地方作为CSubject的一个实例时:

给AbstractTest一个类型参数T,它扩展ISubject,生成类型为T的主题,并在Test中将该参数绑定到CSubject。

答案 3 :(得分:1)

Scala Java无法满足您的要求。当然,Python或Ruby会让你这样做,而且他们会错的(但是,他们再也不会检查你在做什么)。

问题是这样,假设你可以做你想做的事:

class DSubject extends ISubject
Test.subject = new DSubject

您不能禁止该分配,因为Test实施AbstractTestAbstractTest有公共字段。禁用分配会违反AbstractTest的合同。

Test对象上,对CSubject成员的任何访问都会导致错误,因为subject现在包含DSubject

尝试其他方法,例如聚合而不是继承,或代理类。