2012-02-08 114 views
1

我有下面的代码,这部分在Java中,部分在斯卡拉:斯卡拉:继承接口成员初始化

的Java:

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(); 
    } 
} 

斯卡拉:

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对其进行处理。

回答

3

Java字段subject是可变的,因此它不能在Scala中使用val覆盖。此外,可分配的,不同的变化是不安全的。

您可以通过将subject作为抽象方法来修复代码。这可以与val共同覆盖,就像你在Scala代码中一样。

abstract class AbstractTest { 
    public abstract ISubject subject(); 

    //... 

    public CSubject generateParticularSubject() { 
    return new CSubject(); 
    } 
} 
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返回类型())

+0

回答你的问题,我可以说,我试图达到的目标是将'subject'的一些核心处理放到'AbstractTest'类中。但是该类的孩子必须能够将其初始化为更窄的类实例,并定义更多的方法来处理它,这也适合于特定的子类。同时,'AbstarctTest'类中定义的核心方法应该仍然能够完成将'subject'视为更广泛的'ISubject'的任务。 – noncom 2012-02-08 12:18:09

+1

据我所知,在Java变量不会超过,而是他们得到阴影。 – noncom 2012-02-08 12:19:00

+0

AbstractTest类意识到CSubject类型 - 从generateParticularSubject()方法的声明开始 - 为什么不将'AbstractTest.subject'声明为CSubject而不是ISubject?其次,正如你所说的,Java中的成员字段比被覆盖的字段盖过了阴影,如果你想要走这条路线,那么'AbstractTest.subject'应该用私有可见性来声明,'Test'应该有它自己的'private val subject更窄的类型。 – elk 2012-02-08 12:24:33

2

我想你两个选项。

简单的一个:

object Test extends AbstractTest { 

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

    csubject.b() 
} 

的更好的一个,尤其是当你有,你要访问的主题为CSubject的实例不同的地方:

给AbstractTest其延伸的类型参数T ISubject,使类型为T的主题,并在Test中将该参数绑定到CSubject。

1

什么你问在任斯卡拉 Java是不可能的。当然,Python或Ruby会让你这样做,他们会错误的(但是,他们再一次不检查你在做什么)。

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

class DSubject extends ISubject 
Test.subject = new DSubject 

因为Test实现AbstractTest你不能禁止该分配和AbstractTest有一个公共领域。禁用分配将违反AbstractTest的合同。

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

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