2013-06-24 18 views
30

從2.10開始,-Xlint抱怨包對象內定義的類。但爲什麼?在包對象中定義一個類應該完全等同於在一個單獨的包中使用相同的名稱定義類,除非更方便。爲什麼Scala包中的類不被優先使用?

在我看來,在斯卡拉的嚴重設計缺陷之一是不能把比在文件的頂層一類類似實體上(例如變量聲明,函數定義)的任何其他。相反,你不得不把它們放到一個單獨的'包對象'中(通常在package.scala),與他們所屬代碼的其餘部分分開,並且違反了一個基本的編程規則,這個規則是概念上相關的代碼應該是物理的相關的。我看不出有任何理由斯卡拉不能讓概念在頂層東西,它允許在較低的水平,以及任何非類像自動被放置在包裝的對象,使用戶不必擔心。

例如,在我的情況下,我有一個util包,並根據它我有一些子包(util.ioutil.textutil.timeutil.osutil.mathutil.distances等)組的函數,類異構集合和有時在語義上相關的變量。我目前存儲所有的各種功能,類等的package object坐在稱爲io.scalatext.scala或任何文件,在util目錄。由於功能和類可以混合的方式,這很好,而且非常方便。我可以這樣做:

package object math { 
    // Coordinates on a sphere 

    case class SphereCoord(lat: Double, long: Double) { ... } 

    // great-circle distance between two points 
    def spheredist(a: SphereCoord, b: SphereCoord) = ... 

    // Area of rectangle running along latitude/longitude lines 
    def rectArea(topleft: SphereCoord, botright: SphereCoord) = ... 

    // ... 
    // ... 

    // Exact-decimal functions 
    class DecimalInexactError extends Exception 

    // Format floating point value in decimal, error if can't do exactly 
    formatDecimalExactly(val num: Double) = ... 

    // ... 
    // ... 
} 

沒有這一點,我會很不方便根據樂趣與類,而不是通過語義分割的代碼了。我猜想,另一種方法是將它們放在一個普通的對象中 - 這種方法首先打破了打包對象的目的。

回答

26

但是爲什麼?在包對象中定義一個類應該完全等同於定義具有相同名稱的單獨包中的類,

準確地說。語義(當前)是相同的,所以如果你喜歡在包對象中定義一個類,應該有一個很好的理由。但現實情況是,至少有一個不錯的理由不可以(繼續閱讀)。

除了方便多了

如何是更方便? 如果你這樣做:

package object mypkg { 
    class MyClass 
} 

可以一樣好執行以下操作:

package mypkg { 
    class MyClass 
} 

你甚至會在這個過程中:)

保存一些字符現在,良好而具體的理由不要過分地使用包裝物件,雖然包裝是開放,包裝物件不是。 一種常見的情況是已經派出幾個項目中的代碼,每個項目確定在同一個包中的類。這裏沒問題。另一方面,封裝對象(像任何對象)是封閉的(正如規範所說的那樣「每個封裝只能有一個封裝對象」)。換句話說, 您只能在一個項目中定義包對象。 如果您嘗試在兩個不同的項目中爲同一個包定義包對象,將會發生不好的事情,因爲實際上最終會生成兩個相同JVM類的不同版本(在我們的情況下,最終會出現兩個「 mypkg.class「文件)。 根據具體情況,最終編譯器會抱怨說找不到您在軟件包對象的第一個版本 中定義的內容,或者得到「錯誤的符號引用」錯誤,甚至可能會出現運行時錯誤。這是包對象的一般限制,所以您必須注意它。 在定義包對象內的類的情況下,解決方案很簡單:不要這麼做(因爲與僅將類定義爲頂級相比,您不會獲得任何實質性內容)。 對於類型別名,vals和vars,我們沒有這樣的luxuary,所以在這種情況下,它是衡量句法便利性(與定義它們在對象中相比)是否值得,然後不要小心定義重複的包對象。

+5

您誤解了我的投訴。如果我有一個包含類和函數的實用程序包,那麼(a)我必須將類和函數分開,前者放在一個包中,後者放在一個名稱相同的包對象中,或者我把它們放在一起包對象,這是「棄用」。分離的問題是,它使得不可能將相關的包和功能保持在一起。 –

+0

我重寫了問題以使問題更清楚。 –

+0

儘管包對象未打開,但其相應的包仍然存在。我大量使用包對象(它們是其他編程語言中的模擬模塊),並且尚未遇到您描述的任何問題。 –

1

我還沒有找到一個很好的答案,爲什麼這個語義等價的操作會產生一個皮棉警告。這是一個不起眼的錯誤。我已發現,不能被放置在封裝對象內(相對於一個普通的封裝內)的唯一事情是實現main(或extends App)的對象。

注意-Xlint也抱怨包對象內聲明的隱含類,即使他們不能在包的範圍進行聲明。 (見http://docs.scala-lang.org/overviews/core/implicit-classes.html關於隱性課程的規則。)

-1

我想出一招,允許包對象的沒有關於棄用投訴的所有好處。在地方的

package object foo { 
    ... 
} 

你可以做

protected class FooPackage { 
    ... 
} 

package object foo extends FooPackage { } 

的工作方式相同,但沒有投訴。清楚表明投訴本身是虛假的。

+4

不,除了編譯器有限制並且無法捕捉所有內容這一事實外,沒有其他任何信息了。這首先沒有提到警告的有效性。一個警告就是:編譯器告訴你**在執行某些操作時可能會遇到問題(在這裏你確實可以;你是否正確地閱讀了我的答案?)。 –

相關問題