【问题标题】:Scala: inherited interface member initializationScala:继承的接口成员初始化
【发布时间】:2012-02-08 11:49:26
【问题描述】:

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

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

【问题讨论】:

    标签: java scala inheritance interface overriding


    【解决方案1】:

    Java 字段 subject 是可变的,因此在 Scala 中不能使用 val 覆盖它。此外,由于是可分配的,因此以协变方式改变它是不安全的。

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

     abstract class AbstractTest {
       public abstract ISubject subject();
    
       //...
    
       public CSubject generateParticularSubject() {
         return new CSubject();
       }
     }
    

    【讨论】:

      【解决方案2】:

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

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

      我不明白的是,如何获得与 Java 中相同的行为,在实现类的接口成员时,可以使用任何实现该接口的类对其进行初始化。

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

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

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

      【讨论】:

      • 回答你的问题,我可以说我想要实现的 - 是将subject 的一些核心处理放入AbstractTest 类。但是该类的子类必须能够将其初始化为更窄的类实例并定义更多使用它的方法,这也适合该特定子类。同时,AbstarctTest 类中定义的核心方法应该仍然能够完成将subject 视为更广泛的ISubject 的任务。
      • 据我了解,Java 中的变量不会被覆盖,而是被遮蔽。
      • AbstractTest 类知道 CSubject 类型——它遵循 generateParticularSubject() 方法的声明——那么为什么不将 AbstractTest.subject 声明为 CSubject,而不是 ISubject?其次,正如你所说的那样,Java 中的成员字段被掩盖而不是被覆盖,如果你想走这条路,那么AbstractTest.subject 应该真正声明为私有可见性,Test 应该有自己的更窄类型的private val subject .
      • 查看问题中的更新。最初定义为ISubject的东西,在AbstractTest的多个子类中可能会变成CSubjectCMySubjectCTheirSubject等。尽管如此,来自 AbstractTest 的方法应该能够处理所有这些,就好像它们是 ISubject-s 一样。也许这是一个架构缺陷?
      • 看到您对问题的更新。如果您希望 AbstractClass 完全处理 ISubject 我相信您应该将 AbstractClass.generateParticularSubject() 的返回类型声明为 ISubject ,否则您将违反您自己强加的关注点分离。然后我认为你应该使用遮蔽而不是覆盖,就像 AbstractTest 和 Test 都是用 Java 编写的那样。
      【解决方案3】:

      我认为你有两个选择。

      最简单的:

      object Test extends AbstractTest {
      
        val csubject: CSubject = generateParticularSubject() 
        override val subject = csubject
      
        csubject.b() 
      }
      

      更好的一个,尤其是当您有多个地方想要访问主题作为 CSubject 的实例时:

      给 AbstractTest 一个扩展 ISubject 的类型参数 T,使主题为 T 类型,并在测试中将该参数绑定到 CSubject。

      【讨论】:

        【解决方案4】:

        您所要求的在 Scala Java 中都是不可能的。当然,Python 或 Ruby 会让你这样做,但它们是错误的(但话又说回来,它们根本不会检查你在做什么)。

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

        class DSubject extends ISubject
        Test.subject = new DSubject
        

        您不能禁止该分配,因为Test 实现了AbstractTest,并且AbstractTest 有一个公共字段。禁用分配将违反AbstractTest 的合同。

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-04-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-27
          • 1970-01-01
          相关资源
          最近更新 更多