Java Scala:继承的接口成员初始化
我有以下代码,部分是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;值主题的类型不兼容。如果删
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
尝试其他方法,例如聚合而不是继承,或者代理类。您所要求的是不可能的