Java Scala:继承的接口成员初始化

Java Scala:继承的接口成员初始化,java,scala,inheritance,interface,overriding,Java,Scala,Inheritance,Interface,Overriding,我有以下代码,部分是Java代码,部分是Scala代码: 爪哇: 斯卡拉: object Test extends AbstractTest { override val subject: CSubject = generateParticularSubject() /*1*/ subject.b() /*2*/ } 问题是:如果标记为“1”的行与上面代码中的行相同,编译器会抱怨在IsSubject类型的类AbstractTest中重写变量subject;值主题的类型不兼容。如果删

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

爪哇:

斯卡拉:

object Test extends AbstractTest {

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

  subject.b() /*2*/
}
问题是:如果标记为“1”的行与上面代码中的行相同,编译器会抱怨
在IsSubject类型的类AbstractTest中重写变量subject;值主题的类型不兼容
。如果删除第“1”行中的类型注释
:CSubject
,以及关键字
override val
,则该错误将消失,但第“2”行会出现另一个错误:
值b不是ISubject的成员

我理解这些错误是怎么说的,以及编译器如何看待这些问题。我不明白的是,如何获得与Java中相同的行为,在Java中,实现类的接口成员时,可以使用实现接口的任何类型的类对其进行初始化。在Scala中实现这一点的方法是什么


更新:在Scala中不可能实现这样的构造是真的吗?我之所以要这样做,是因为
AbstractTest
包含处理subject的
ISubject
端的方法,而应该重写
subject
并将其定义为更窄的类实例的子类必须实现特定于该类的方法。同时,我希望
AbstractTest
继续处理与
主题相关的所有问题,并且在通过其接口
ISubject
处理时可以处理这些问题。目前还不清楚您在
测试对象中试图实现什么。如果只需要将
generateParticularSubject()
方法的结果作为
CSubject
的实例使用,只需为变量使用不同的名称,即

object Test extends AbstractTest {
  subj = generateParticularSubject()
  subj.b()
}
我不明白的是,如何获得与Java中相同的行为,在Java中,实现类的接口成员时,可以使用实现接口的任何类型的类对其进行初始化

您在这里所指的被称为“协变返回类型”,并且从版本5开始在Java中就得到了支持(在子类中重写的方法可以返回更窄的类型)。然而,您在Scala代码中尝试的操作与协变返回类型无关,相反,您尝试重写基类的成员字段,这确实不是一个好主意。该字段在基类中声明为具有类型IsSubject,并且应该保持这种状态。这就是为什么你可以写:

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

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

不清楚您试图在
测试对象中实现什么。如果只需要将
generateParticularSubject()
方法的结果作为
CSubject
的实例使用,只需为变量使用不同的名称,即

object Test extends AbstractTest {
  subj = generateParticularSubject()
  subj.b()
}
我不明白的是,如何获得与Java中相同的行为,在Java中,实现类的接口成员时,可以使用实现接口的任何类型的类对其进行初始化

您在这里所指的被称为“协变返回类型”,并且从版本5开始在Java中就得到了支持(在子类中重写的方法可以返回更窄的类型)。然而,您在Scala代码中尝试的操作与协变返回类型无关,相反,您尝试重写基类的成员字段,这确实不是一个好主意。该字段在基类中声明为具有类型IsSubject,并且应该保持这种状态。这就是为什么你可以写:

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

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

我认为您有两个选择

简单的一点是:

object Test extends AbstractTest {

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

  csubject.b() 
}
更好的一个,尤其是当您在不同的位置希望访问subject作为CSubject实例时:


为AbstractTest提供一个扩展IsSubject的类型参数T,将subject设置为类型T,并在测试中将该参数绑定到CSubject。

我认为您有两个选择

简单的一点是:

object Test extends AbstractTest {

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

  csubject.b() 
}
更好的一个,尤其是当您在不同的位置希望访问subject作为CSubject实例时:


为AbstractTest提供一个扩展IsSubject的类型参数T,使subject为类型T,并在测试中将该参数绑定到CSubject。

Java字段
subject
是可变的,因此不能在Scala中使用
val来覆盖它。而且,由于是可分配的,所以将其进行协变是不安全的

您可以通过将
subject
设置为抽象方法来修复代码。这可以用
val
协变地覆盖,就像您在Scala代码中所做的那样

 abstract class AbstractTest {
   public abstract ISubject subject();

   //...

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

Java字段
subject
是可变的,因此不能使用
val
在Scala中重写它。而且,由于是可分配的,所以将其进行协变是不安全的

您可以通过将
subject
设置为抽象方法来修复代码。这可以用
val
协变地覆盖,就像您在Scala代码中所做的那样

 abstract class AbstractTest {
   public abstract ISubject subject();

   //...

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

您所要求的在Scala或Java中都是不可能的。当然,Python或Ruby会允许您这样做,但它们是错误的(但是,它们根本不会检查您正在做什么)

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

class DSubject extends ISubject
Test.subject = new DSubject
您不能禁止该赋值,因为
Test
实现了
AbstractTest
,并且
AbstractTest
有一个公共字段。禁用分配将违反
AbstractTest
的合同

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


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

您所要求的是不可能的