我有以下代码,部分使用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
处理时进行处理。
答案 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
实施AbstractTest
,AbstractTest
有公共字段。禁用分配会违反AbstractTest
的合同。
在Test
对象上,对CSubject
成员的任何访问都会导致错误,因为subject
现在包含DSubject
。
尝试其他方法,例如聚合而不是继承,或代理类。