2014-12-06 77 views
1

我已經使用了這幾種不同的方法...但還沒有找到明確的答案。在子類中重新定義一個變量類型(即變量隱藏)

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的需要'越權」修飾」,但你也不能覆蓋變種。

在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變量,而不必使用醜陋的投射 - 如果我只在單獨的foo中聲明瞭這種情況Bar1類。

謝謝!

+0

我不知道C#,可以在你的第二個代碼片段中賦值'foo'嗎? – Dimitri 2014-12-06 23:46:45

+0

@Dimitri是的,如果你的意思是說x是Bar2的一個實例,那麼x.foo = new Foo2()是有效的代碼......但是這個例子有點過分,因爲這不是我在應用程序中使用它的原因我正在從C#移動到Scala。我會在這裏留下另一條消息或編輯我的問題來解釋更多爲什麼/我需要什麼。 – jross 2014-12-07 00:12:57

回答

2

看起來您正在尋找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#代碼明白的是,你真的有2場中的一個實例Bar2。當看作Bar1時,x.foo是指來自Bar1的字段,並且看作Bar2,x.foo涉及Bar2中的屬性。而他們的類型甚至他們的本質(方法/領域/類型)可以完全無關。我想不出在斯卡拉類似的東西。但如果我是正確的,你可以使用另一個名字Bar2foo


更新2

下面的代碼可能是你想要的東西,如果foo永遠不會重新分配:

class Bar1(val foo: Foo1) 

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

嗯...我得仔細研究你的答案,看看它是否適用於我爲什麼需要這個。 – jross 2014-12-07 00:12:31

+0

啊..是的。我認爲你理解正確。在我的編輯中,我展示瞭如何以及爲什麼要這樣做。我仍在審查和測試...所以如果我正確地理解你。 「可變隱藏」的想法在C#中可用(我也非常確定Java),但不是Scala? – jross 2014-12-07 01:09:17

+0

更新2很有趣 - 它可能會訣竅。我會嘗試一下和其他排列。我還沒有想到Scala的構造器怪異點。我剛剛開始在昨天看斯卡拉。 +1努力。我會在一兩天內將它標記爲正確的答案,如果沒有其他人在使用Scala編寫「可變隱藏」的簡單方法時發出響應 – jross 2014-12-07 02:10:56

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" } 
            ^

有些人主張更強的隱私權利。

+0

我喜歡那裏的最後一句話!好的! +1 :)基本上,我認爲控制Scala的決策者僅僅是反對變量隱藏在子類中。期。將你的兩行代碼片段轉換爲C#逐字'class A {public int v = 42; } class B:{private string v =「42」; }'在C#中工作得很好(你會得到一個警告,你隱藏了v,但是把new關鍵字放在'class B:{private private string v =「42」;}'並且警告消失了) 。 – jross 2014-12-07 21:15:13