28-pkgobj-a

パッケージオブジェクト (package_objects)

Martin_Odersky, Lex Spoon
September 15, 2010

2.8 まで、あなたがパッケージに入れることができた唯一のものはクラス、トレイトとスタンドアローンのオブジェクトでした。パッケージのトップレベルには最もよく使われる定義が置いてあります。しかし Scala プログラミング言語バージョン 2.8 にできることは、それに留まりません。クラス内のどのような種類の定義も、パッケージのトップレベルに置けます。もしパッケージ全体のスコープに入れたいヘルパーメソッドがあるなら、どうぞパッケージのトップレベルの適切な場所に入れてください。

そうするには、パッケージオブジェクトの中に定義を入れます。各パッケージは 1 つのパッケージオブジェクトを持てます。パッケージオブジェクト中に置かれたどのような定義も、パッケージそれ自身のメンバーとみなされます。

次に例をあげます。まず、パッケージ gardening.fruits にクラス Fruit と 3 つの Fruit オブジェクトがあるとします。:

   // ファイル gardening/fruits/Fruit.scala 中
   package gardening.fruits
   case class Fruit(name: String, color: String)
   object apple extends Fruit("Apple", "green")
   object plum extends Fruit("Plum", "blue")
   object banana extends Fruit("Banana", "yellow") 

今度は、パッケージ gardening 中に直接、変数 planted とメソッド showFruit を置きたいとします。次の様にします。:

   // ファイル gardening/fruits/package.scala 中
   package gardening
   package object fruits {
     val planted = List(apple, plum, banana)               
     def showFruit(fruit: Fruit) {
       println(fruit.name +"s are "+ fruit.color)
     }
   }

ファイル gardening/fruits/package.scala は、パッケージ gardening.fruits のパッケージオブジェクトを保持します。構文的に、パッケージオブジェクトは単純なオブジェクト定義とほとんど同じように見えます。唯一の違いは、それがキーワード package を含むということです。これは、単純なオブジェクトではなく、パッケージオブジェクトを表します。波括弧の中に、あらゆる定義を含めることができます。上の場合では、パッケージオブジェクトは planted 変数と showFruit ユーティリティメソッドを含んでいます。

そのような定義を与えられていれば、同じパッケージ中の他のいかなるコードも、クラスをインポートするのと全く同じようにして、メソッドをインポートできます。例えば、次のオブジェクト PrintPlanted は、パッケージ gardening.fruits 上のワイルドカード・インポートを使って、クラス Fruit をインポートするのと全く同じやり方で planted と showFruit をインポートします。

   // ファイル PrintPlanted.scala 中
   import gardening.fruits._
   object PrintPlanted {
     def main(args: Array[String]) {
       for (fruit: Fruit <- fruits.planted) {
         showFruit(fruit)
       }
     }
   }

パッケージオブジェクトは、変数やメソッド定義だけではなく、任意の定義を含むことができます。例えば、それらはパッケージ全体にわたる型エイリアスや暗黙の変換を保持するのにも頻繁に使われます。パッケージオブジェクトは Scala クラスやトレイトの継承さえできます。


==========================================


パッケージオブジェクトをどこに入れるか

(Where to put package objects)

Package objects are compiled to class files named package.class which are the located in the directory of the package that they augment. So the package object fruits would be complied to a class with fully qualified name gardening.fruit.package (Note that, even though package is a reserved word in Java and Scala, it is still allowed as part of a class name on the JVM level. In Scala, you can even define it directly, using backticks:
パッケージオブジェクトは、package.class という名前のクラスファイルへコンパイルされ、増やしたパッケージのディレクトリ中に置かれます。ですから、パッケージオブジェクト fruits は、完全修飾された名前 gardening.fruit.package をもつクラスへコンパイルされます(package は Java と Scala の予約語ですが、JVM レベルではクラス名の一部としても使えます)。Scala では、バッククオートを使って、直接これを定義することさえできます。:

   package gardening.fruits
   object `package` { ... }

ソースファイルと同じ記法を使えるのは便利です。このように通常、パッケージオブジェクト gardening.fruits のソースファイルは、gardening/fruits ディレクトリ中にある package.scala と名付けられたファイルへ入れます。

==========================================


Scala パッケージオブジェクト


標準的な Scala パッケージもそのパッケージオブジェクトを持っています。すべての Scala ファイル中に scala._ が自動的にインポートされるので、このオブジェクトの定義は前置子なしで利用できます。

次は、このパッケージオブジェクトで最も重要な定義です。ご覧のように、このオブジェクトの主目的は、サブパッケージ中にネストされた多数のよく使われる定義を Scala パッケージから利用可能にすることです。このようにして、我々はアクセスの便宜と論理構造を分離します。例えば、List 型は大変よく使うので、これを Scala パッケージ中に置いて、インポートあるいは名前修飾なしでアクセスできるようにすることは、意味があります。他方、List はイミュータブルなコレクションクラスですから、論理的にはパッケージ scala.collection.immutable に属します。パッケージオブジェクトを使って、論理構造の維持と高速アクセスの両立ができます。
 package object scala {
   
   // コレクションクラスの型と値のエイリアス
   
   type TraversableOnce[+A] = scala.collection.TraversableOnce[A] 
     
   type Traversable[+A] = scala.collection.Traversable[A]
   val Traversable = scala.collection.Traversable
     
   type Iterable[+A] = scala.collection.Iterable[A]
   val Iterable = scala.collection.Iterable
     
   type Seq[+A] = scala.collection.Seq[A]
   val Seq = scala.collection.Seq
       
   type IndexedSeq[+A] = scala.collection.IndexedSeq[A]
   val IndexedSeq = scala.collection.IndexedSeq
     
   type Iterator[+A] = scala.collection.Iterator[A]
   val Iterator = scala.collection.Iterator
     
   type BufferedIterator[+A] = scala.collection.BufferedIterator[A]
   
   type List[+A] = scala.collection.immutable.List[A]
   val List = scala.collection.immutable.List
   
   val Nil = scala.collection.immutable.Nil
   
   type ::[A] = scala.collection.immutable.::[A]
   val :: = scala.collection.immutable.::
   
   type Stream[+A] = scala.collection.immutable.Stream[A]
   val Stream = scala.collection.immutable.Stream
   val #:: = scala.collection.immutable.Stream.#::
   
   type Vector[+A] = scala.collection.immutable.Vector[A]
   val Vector = scala.collection.immutable.Vector
    
   type StringBuilder = scala.collection.mutable.StringBuilder
   
   type Range = scala.collection.immutable.Range
   val Range = scala.collection.immutable.Range
   
   // scala.math.* へ移された数値型
   
   type BigDecimal = scala.math.BigDecimal
   val BigDecimal = scala.math.BigDecimal
    
   type BigInt = scala.math.BigInt
   val BigInt = scala.math.BigInt
    
   type Equiv[T] = scala.math.Equiv[T]
   type Fractional[T] = scala.math.Fractional[T]
   type Integral[T] = scala.math.Integral[T]
  
   type Numeric[T] = scala.math.Numeric[T]
   val Numeric = scala.math.Numeric
    
   type Ordered[T] = scala.math.Ordered[T]
   val Ordered = scala.math.Ordered
  
   type Ordering[T] = scala.math.Ordering[T]
   val Ordering = scala.math.Ordering
    
   type PartialOrdering[T] = scala.math.PartialOrdering[T]    
   type PartiallyOrdered[T] = scala.math.PartiallyOrdered[T]
   ...
 }

package scala の型エイリアスの大部分に、同じ名前の値エイリアスが一緒にあることにも注意してください。例えば、List クラスの型エイリアスと List オブジェクトの値エイリアスがあります。このようにして、前置子なしでリスト型にアクセスできるだけではなく、List(1,2,3) のような構文でリスト値の生成もできます。後者の式を分解すると、次になります。

   List.apply(1, 2, 3)

すなわち、scala.List 値の apply メソッドの呼び出しです。この値は今度は、当該の apply メソッドを定義する scala.collection.immutable.List オブジェクトのエイリアスです。

==========================================


制限事項と次にすること (Restrictions and things to come)


Package objects as currently implemented have some restrictions. First, you cannot define or inherit overloaded methods in package objects. Second, you cannot define or inherit a member in a package object which is also the name of a top-level class or object in same package.
現在実装されているパッケージオブジェクトには、いくつかの制限があります。第一に、パッケージオブジェクト中のオーバーロードされたメソッドを定義、あるいは継承できません。第二に、同じパッケージ中でトップレベルのクラスまたはオブジェクトの名前にもなる、パッケージオブジェクト中のメンバーを定義あるいは継承できません。我々は近い将来の Scala リリースで、これらの制限がなくなることを期待しています。

興味深い問題は、パッケージ中のトップレベルのクラスまたはオブジェクトの名前が、関連するパッケージオブジェクト中で再定義されたとき何が起きるかです。当面、これは単に禁じられています。

But if we wanted to allow this, it would make sense to take the definition in the package_object as the public interface of the definition in the package itself.
しかしもしこれを許したいなら、パッケージオブジェクト中の定義をパッケージそれ自身の定義の公開インターフェースとみなすことは理にかなっているでしょう。パッケージ内のコードからは、完全な定義を見ることができます。しかしパッケージ外のコードからは、パッケージオブジェクト中で何が定義されているかが見えるだけです。このようにパッケージオブジェクトは、インターフェースと実装を柔軟に組み合わせることを可能にする、かなり先進的なモジュールシステムのバックボーンに発展し得ます。

もちろん、まだすべきことはあり、詳細を練らねばなりません。しかしこれは興味深い可能性を切り開きます。

==========================================

目次 (Contents)


==========================================

ナビゲーション (Navigation)

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2011年04月07日 09:54
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。