アットウィキロゴ

28-pkgobj-a

「28-pkgobj-a」の編集履歴(バックアップ)一覧に戻る

28-pkgobj-a - (2011/04/07 (木) 09:54:37) のソース

* パッケージオブジェクト (package_objects)
&bold(){Martin_Odersky, Lex Spoon}
&bold(){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 クラスやトレイトの継承さえできます。


- &link_anchor(where){パッケージオブジェクトをどこに入れるか} 
- &link_anchor(packageobj){Scala パッケージオブジェクト}
- &link_anchor(restrictions){制限事項と次にすること}
- &link_anchor(contents){目次}
- &link_anchor(navigation){ナビゲーション}
#center(){==========================================}


&aname(where,option=nolink){ }
** パッケージオブジェクトをどこに入れるか
(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 と名付けられたファイルへ入れます。

#center(){==========================================}


&aname(packageobj,option=nolink){ }
** 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 オブジェクトのエイリアスです。

#center(){==========================================}


&aname(restrictions,option=nolink){ }
** 制限事項と次にすること (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.
しかしもしこれを許したいなら、パッケージオブジェクト中の定義をパッケージそれ自身の定義の公開インターフェースとみなすことは理にかなっているでしょう。パッケージ内のコードからは、完全な定義を見ることができます。しかしパッケージ外のコードからは、パッケージオブジェクト中で何が定義されているかが見えるだけです。このようにパッケージオブジェクトは、インターフェースと実装を柔軟に組み合わせることを可能にする、かなり先進的なモジュールシステムのバックボーンに発展し得ます。

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

#center(){==========================================}

&aname(contents,option=nolink){ }
** 目次 (Contents)
- &link_anchor(where){パッケージオブジェクトをどこに入れるか} 
- &link_anchor(packageobj){Scala パッケージオブジェクト}
- &link_anchor(restrictions){制限事項と次にすること}
- &link_anchor(contents){目次}
- &link_anchor(navigation){ナビゲーション}

#center(){==========================================}

&aname(navigation,option=nolink){ }
** ナビゲーション (Navigation)
- &link_anchor(where){パッケージオブジェクトをどこに入れるか} 
- &link_anchor(packageobj){Scala パッケージオブジェクト}
- &link_anchor(restrictions){制限事項と次にすること}
- &link_anchor(contents){目次}
- &link_anchor(navigation){ナビゲーション}
ツールボックス

下から選んでください:

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