パッケージオブジェクト


92

パッケージオブジェクトとは何ですか?概念ではなく、その使用方法は?

私は例を動作させるように試みました、そして私が動作するようになった唯一のフォームは以下の通りです:

package object investigations {
    val PackageObjectVal = "A package object val"
}

package investigations {

    object PackageObjectTest {
        def main(args: Array[String]) {
            println("Referencing a package object val: " + PackageObjectVal)
        }
    }
}

これまでに私が行った観察は次のとおりです。

package object _root_ { ... }

禁止されています(これは妥当です)、

package object x.y { ... }

また、許可されていません。

パッケージオブジェクトは直接の親パッケージで宣言する必要があるようです。上記のように記述した場合、ブレース区切りのパッケージ宣言フォームが必要です。

それらは一般的に使用されていますか?もしそうなら、どうですか?



1
@Brent、これは、パッケージオブジェクトの記事だけでなく、優れたリソースです。著者のことを聞いたことがありますが、彼がこのScalaツアーを書いたことに気付きませんでした。
ドンマッケンジー

回答:


128

通常、パッケージオブジェクトは、package.scala対応するパッケージで呼び出される別のファイルに配置します。ネストされたパッケージ構文を使用することもできますが、これは非常に珍しいことです。

パッケージオブジェクトの主な使用例は、パッケージによって定義されたAPIを使用するときに、パッケージの内部だけでなくパッケージの外部のさまざまな場所で定義が必要な場合です。次に例を示します。

// file: foo/bar/package.scala

package foo

package object bar {

  // package wide constants:
  def BarVersionString = "1.0"

  // or type aliases
  type StringMap[+T] = Map[String,T]

  // can be used to emulate a package wide import
  // especially useful when wrapping a Java API
  type DateTime = org.joda.time.DateTime

  type JList[T] = java.util.List[T]

  // Define implicits needed to effectively use your API:
  implicit def a2b(a: A): B = // ...

}

これで、そのパッケージオブジェクト内の定義がパッケージ全体で利用可能になります foo.bar。さらに、そのパッケージ外の誰かがインポートすると、定義がインポートされますfoo.bar._

このようにして、ライブラリを効果的に使用するためにAPIクライアントが追加のインポートを発行することを要求しないようにすることができます。

import swing._
import Swing._

onEDTからTuple2への暗黙的な変換のようなすべての優れた機能を備えていますDimension


13
警告:メソッドのオーバーロードはパッケージオブジェクトでは機能しません。
レトロネーム2010

パッケージオブジェクトをパッケージ階層の1レベル上に定義する必要があることが選択された理由に打ち勝ちます。たとえば、仮想orgまたはcom最上位のパッケージを独自のルートパッケージに所属させたい場合は、パッケージオブジェクトで汚染する必要がありますorg.foo。定義をパッケージの直下に含めることを許可すると、それが含まれるはずです-言語APIインターフェースが少し適切になったはずです。
Matanster 2015年

58

Moritzの答えは適切ですが、もう1つ注意すべき点は、パッケージオブジェクトはオブジェクトであることです。特に、これは、ミックスイン継承を使用して、特性からそれらを構築できることを意味します。モリッツの例は次のように書くことができます

package object bar extends Versioning 
                          with JodaAliases 
                          with JavaAliases {

  // package wide constants:
  override val version = "1.0"

  // or type aliases
  type StringMap[+T] = Map[String,T]

  // Define implicits needed to effectively use your API:
  implicit def a2b(a: A): B = // ...

}

ここで、バージョニングは、パッケージオブジェクトに「バージョン」メソッドが必要であることを示す抽象的な特性ですが、JodaAliasesおよびJavaAliasesは、便利な型のエイリアスを含む具体的な特性です。これらの特性はすべて、さまざまなパッケージオブジェクトで再利用できます。


トピック全体が大きく開放されており、他の豊富な例のおかげで、それは完全な可能性に慣れているようです。
Don Mackenzie

1
しかし、それらはvalとして使用できないため、実際にはオブジェクトではありません
Eduardo Pareja

7

@Alex Cruise、ありがとうございます。これは、別のコンパイルユニットが必要であることを示唆しているようです(おそらく、中括弧で区切られたパッケージ制限を回避します)。問題は、私がそれらをどのように使用するかについての私自身の推測ではなく、いくつかのしっかりしたユーザーアドバイスが欲しいことです。
Don Mackenzie

5

パッケージオブジェクトの主な使用例は、パッケージによって定義されたAPIを使用するときに、パッケージの内部だけでなくパッケージの外部のさまざまな場所で定義が必要な場合です。

それほどでScalaの3、半ば2020年にリリースされる予定に基づいて義母として、ここで

トップレベルの定義

あらゆる種類の定義をトップレベルで書くことができます。
パッケージオブジェクトは不要になり、段階的に廃止されます。

package p 

type Labelled[T] = (String, T) 
val a: Labelled[Int] = ("count", 1) 
def b = a._2 
def hello(name: String) = println(i"hello, $name)

@VonCに感謝します。これと他の多くの理由でScala 3を本当に楽しみにしています。私はパッケージオブジェクトをあまり使用していませんが、トップレベルの定義を使用すると確信しています。
Don Mackenzie
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.