【问题标题】:Redefine a variable type in a subclass (i.e. variable hiding)在子类中重新定义变量类型(即变量隐藏)
【发布时间】:2014-12-06 23:12:29
【问题描述】:

我已经用谷歌搜索了几种不同的方法……但还没有找到明确的答案。

class Foo1 { }
class Foo2 extends Foo1 { }

class Bar1 {
    var foo: Foo1 = null
}

class Bar2 extends Bar1 {
    var foo: Foo2 = null  // <-- compiler error
}

错误提示:“覆盖 test.Foo1 类型的 Bar1 类中的变量 foo;变量 foo 需要 `override' 修饰符”但您也不能覆盖 var。

在 C# 中,您可以像这样使用 new 关键字

class Foo1 { }
class Foo2 : Foo1 { }

class Bar1
{
    public Foo1 foo = null;
}

class Bar2 : Bar1 
{
    public new Foo2 foo = null;
}

在 Scala 中可以做到这一点吗?

编辑 -- 添加了另一个 C# 示例来说明我这样做的原因。

class Foo1 { }
class Foo2 : Foo1 { }

class Bar1
{
    public Foo1 Foo { get { return _foo; } }
    protected Foo1 _foo;
    public Bar1(Foo1 foo) { _foo = foo; }       
}

class Bar2 : Bar1
{
    public new Foo2 Foo { get { return _foo; } }
    protected new Foo2 _foo;
    public Bar2(Foo2 foo) : base(foo) { _foo = foo; }
}

当然,在真正的应用程序中,类要复杂得多,成员很多,层次结构更深。

简而言之,这对我来说是它允许我在我的类层次结构中的任何地方使用 foo 变量,而不必使用丑陋的演员表——如果我只在酒吧1级。

谢谢!

【问题讨论】:

  • 我不懂 C#,foo 可以在你的第二个代码 sn-p 中赋值吗?
  • @Dimitri 是的,如果你的意思是说 x 是 Bar2 的一个实例,那么 x.foo = new Foo2() 是有效代码......但是这个例子有点做作,因为这不是我真正的方式在我正在考虑从 C# 迁移到 Scala 的应用程序中使用它。我将在此处留下另一条消息或编辑我的问题以解释更多为什么/我需要它的原因。

标签: scala


【解决方案1】:

您似乎正在寻找 var 属性的协方差。这不编译,因为它不健全:

class Foo1
class Foo2 extends Foo1 {
  def moo = 42
}
class Foo3 extends Foo1

class Bar1 {
  var foo: Foo1 = null
}

class Bar2 extends Bar1 {
  var foo: Foo2 = null // Let's suppose this compiles
}    

val bar2: Bar2 = new Bar2
val bar1: Bar1 = bar2
bar1.foo = new Foo3
bar2.foo.moo

在最后一行,您在 Foo3 的实例上调用 .moo(它没有实现这种方法),但编译器无法告诉您这一点,因为 bar2.foo 应该是 Foo2

val 上,但是它可以编译:

class Bar1 {
  val foo: Foo1 = new Foo1
}

class Bar2 extends Bar1 {
  override val foo: Foo2 = new Foo2
}

更新

经过一些测试和reading this,我在您的C# 代码中了解到您在Bar2 的实例中确实有2 个字段。当被视为Bar1 时,x.foo 指的是来自Bar1 的字段,被视为Bar2 时,x.foo 指的是Bar2 中的属性。并且它们的类型甚至它们的性质(方法/字段/类型)可以完全不相关。我想不出Scala中有任何类似的东西。但如果我是正确的,你也可以为Bar2foo 使用另一个名称。


更新 2

如果从未重新分配foo,则以下代码可能是您想要的:

class Bar1(val foo: Foo1)

class Bar2(override val foo: Foo2) extends Bar1(foo)

【讨论】:

  • 嗯...我得稍微研究一下你的答案,看看它是否适用于我需要这个的原因。
  • 啊……是的。我想你理解正确。在我的编辑中,我展示了我这样做的方式和原因。我仍在审查和测试……所以如果我理解正确的话。 “变量隐藏”的想法在 C# 中可用(我很确定 Java 也是如此),但在 Scala 中不可用?
  • Update 2 很有趣——它可能会成功。我会试试这个和其他排列。我还没有理解 Scala 构造函数的怪异之处。我昨天才开始研究 Scala。 +1 努力。如果没有其他人用一种简单的方法在 Scala 中进行“变量隐藏”,我会在一两天内将其标记为正确答案
  • 我选择了正确的答案,因为“我在 Scala 中想不出任何类似的东西”。我早就应该检查它了——抱歉花了这么长时间,我忘记了!
【解决方案2】:

这通常表示为类型参数或类型成员。

例如,集合跟踪其底层“表示”或实现:

https://github.com/scala/scala/blob/v2.11.4/src/library/scala/collection/GenSeqLike.scala#L33

它会在以下位置弹出:

https://github.com/scala/scala/blob/v2.11.4/src/library/scala/collection/LinearSeqOptimized.scala#L27

但是成员隐藏机制解决的另一个问题是脆弱的类型层次结构。类必须为继承而设计(Effective Java)。因此,类可以在不考虑与客户端(包括子类)兼容性的情况下发展的概念具有误导性。

这出现在 Scala 中是因为私有成员是如何继承(或不继承)的。我暂时找不到讨论,但in this issue 它被称为weird "private members aren't inherited" change。你可能会问,“这有什么奇怪的?”

This issue 可能是总括问题或元错误。

这是令人烦恼的情况:

scala> class A { val v: Int = 42 }
defined class A

scala> class B extends A { private val v: String = "42" }
<console>:8: error: overriding value v in class A of type Int;
 value v has weaker access privileges; it should not be private
       class B extends A { private val v: String = "42" }
                                       ^

有些人主张加强隐私权。

【讨论】:

  • 我喜欢最后一句话!好一个! +1 :) 基本上,在我看来,控制 Scala 的决策者只是反对隐藏在子类中的变量。时期。将您的两行 sn-p 逐字转换为 C# class A { public int v = 42; } class B : A { private string v = "42"; } 在 C# 中工作得很好(您会收到警告说您正在隐藏 v,但只需将新关键字放在那里 class B : A { private new string v = "42"; } 警告就会消失) .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多