LL脳がscalaの勉強を始めたよ その38


Scalaコップ本の13章に進みますよー、13章はパッケージとインポートですねー

importはPythonで似た様なことをしてたけどpackage宣言はなかったのでちょっと楽しみですな。それにしてもパッケージとインポートは名前空間定義の一種っていう解釈でいいのかしらね…まあ、とりあえずやってみますか

パッケージ

今まで書いてきたサンプルコードはpackege宣言をしてこなかったので”無名パッケージ"に含まれるモノ扱いされているそうです。

なので名前付きパッケージ宣言をとりあえずやってみますよ

// パッケージ宣言デス
package bobsrockets.navigation
// パッケージに含まれるクラスの定義デス
class Navigator

上記はNavigatorクラスはbobsrockets社のnavigationアプリに含まれるよヽ( ・∀・)ノって意味にとれるらしいデスね。Java的逆ドメイン方式にするとしたらcom. bobsrockets.navigationにするらしいですけど、メンド臭いので省略しますとのことでした。

ちなみにbobsrocketsをbobrockに空目したのはMetallica好き故デス

入れ子パッケージ宣言

上記の書き方はJava的表現になっているけども、ScalaC#入れ子パッケージ宣言ができるそうデス。どうも内部的にはこちらの扱い&コップ本的にはこちらがオススメみたいな雰囲気ですね。

とりあえず上でやったの+αを入れ子表現で書いてみますよ

// bobsrocketsのパッケージ宣言
package bobsrockets {
  // bobsrockets.navigatorのパッケージ宣言
  package navigation {
    class Navigator
    // bobsrockets.navigator.testsのパッケージ宣言
    package tests {
      class NavigatorSuite
    }
  }
}

この方法を使うと今まで別々のファイルに記述してたパッケージをまとめて書く事ができるみたいです。入れ子になっている同一パッケージをまとめたり(例えばbobsrockets.navigatorのパッケージとbobsrockets.navigator.testsパッケージを別ファイルにしなくても良くなるのデス)、異なるパッケージを同一ファイルに書いたりイロイロ出来るようになるみたいですね。

まあ、まったく違うパッケージを同じファイルにまとめるのはあまりしないと思いますケド(´・ω・`)


ちなみに大外のbobsrocketsパッケージは内部のパッケージをまとめてるだけで何もしていないのでこんな感じでまとめることも出来るみたいです。

// bobsrocketsはnavigationを含むだけだったのでまとめました
package bobsrockets.navigation {
  class Navigator
  package tests {
    class NavigatorSuite
  }
}

うん、インデント省略的にかなりエコです。

入れ子構造ゆえのアクセス性

Scalaのパッケージ構造は入れ子構造なので、とあるパッケージがソレを含む同ーパッケージ内の他パッケージを参照する際にプレフィックスをつける必要がなかったりします。同一クラス内のメンバー参照みたいなもんですね。

んじゃ、ちょっとサンプル見てみますよー。bobsrockets内に配置されたlaunchパッケージからは同一階層にあるnavigationパッケージに直接アクセスしておりますよー

package bobsrockets {
  // パッケージ宣言です
  package navigation {
    class Navigation
  }
  // navigationパッケージと同一階層にもう一つパッケージ宣言です
  package launch {
    class Booster {
      // bobsrockets.navigation.Navigatorではなくて良いのデス
      val nav = new navigation.Navigator
    }
  }
}
パッケージのスコープ

入れ子になっているためパッケージにもスコープがありマス。言い方を変えると入れ子の性質を利用して同名のパッケージ名を名乗ることが出来るみたいです。逆に内側のスコープのパッケージが外側の同名パッケージを隠蔽してしまうことにもなってしまうみたいですがね(´・ω・`)

それじゃ、パッケージのスコープとソレゾレへのアクセス方法サンプルを書いてみますよー。サンプルでは2つのファイルに各パッケージを記述してみますよー

/*** launch,scalaファイルに書きます ***/
// launchパッケージを定義しますよ
package launch {
  class Booster1
}

/*** bobsrockets.scalaファイルに書きますよー
package bobsrockets {
  // bobsrockets.launchパッケージを定義しますよ
  package launch {
    class Booster2
  }
  package navigation { 
    // bobsrockets.navigation.launchパッケージを定義しますよ
    package launch {
      class Booster3
    }
    class MissionControl {
      // 一番外側(別ファイル)のlaunchにアクセスします
      val booster1 = new _root_.launch.Booster1
      // 二番目に外側にあるlaunchにアクセスします
      val booster2 = new bobsrockets.launch.Booster2
      // 同一パッケージ(bobsrockets.navigation)内のlaunchにアクセスします
      val booster3 = new launch.Booster3
    }
  }
}

bobsrockets.navigation.MissionControlから単純にlaunchにアクセスすると同一階層にあるlunchに向いてしまうので、プレフィクスを付けてアクセスしておりますね。

ちなみに一番外側(launch.scala内)のlaunchにアクセスする場合は頭に_root_をつけることでアクセスできるみたいです。これは最上位パッケージの(launch.scala内の)launch、bobsrocketsの両方が_root_パッケージに含まれるものとして扱われるからみたいです。

インポート

パッケージ宣言をやってみたので今度はインポートしてみますよー、とりあえずインポート用のサンプルパッケージを作成してみますね

// パッケージ宣言デス
package bobsdelights

// 抽象クラスを定義しますよー
abstract class Fruit (val name:String, val color:String)

// スタンドアロンシングルトンオブジェクトを定義しますよ
object Fruits {
  object Apple extends Fruit("apple", "red")
  object Orange extends Fruit("orange", "orange")
  object Pear extends Fruit("pear", "yellowish")
}

bobさんが好きなものパッケージを作っとる( ゚д゚)…気を撮り直してインポートやってみますよ

基本的にインポート

Scalaでの基本的なインポートをやってみますよ

// 特定要素指定の単一型のインポート
import bobsdelights.Fruit

// パッケージ全体(パッケージ内の全てのメンバー)のインポート
import bobsdelights._

// パッケージ内メンバー(オブジェクトとか)の各メンバーを参照するインポート
import bobsdelights.Fruits._

アンダーバー"_"を * 的に使うところ以外はよく見る形式ですねー、ちなみにScalaのimportは任意の位置で可能デス。あとパッケージもオブジェクトも参照できるみたいですねー

インポートするメンバーの限定

インポートの際にインポートセレクター節をつけることでインポートするメンバーを限定することができマスデス

こんな感じですねー

// Fruits内のAppleとOrangeだけ参照しますよ
import Fruit.{Apple, Orange}
インポートするメンバーの名前変更

インポート時にメンバーの名前を変更することができますね

// AppleをMacと名前変更して参照します
import Fruits.{Apple=>Mac, Orange}

こうするとMacを指定することでFruits.Appleを参照することが出来るようになるみたいですねー

これは例えばjavaSQL日付クラスを次のように参照することで、Javaの日付クラスDateと区別することができたりするみたいですー

import java.sql.{Date => SDate}

ちなみにこうやってS.Dateとして使っちゃう、という手もありますな

import java.{sql => S}

とりあえず名前を省略したり重複を避けるためなんかに上手く使うと便利そうデス

特定のメンバーの名前を変えて、他は全てインポート

全メンバーをインポートするのにはこういう文法も使えます

// import Fruits._と同じ意味です
import Fruits.{_}

実際はimport.Fruits._がimport Fruits.{_}の省略形っぽいですね

ちなみに上の名前変更インポートと組み合わせるとAppleの名前を変えてソレ以外は全てそのまま参照する、ってのができますデス

import Fruits.{Apple => Mac, _}
importから除外

とあるパッケージから特定のメンバー以外を参照したい場合は次のようにします

// Apple以外のメンバーを参照します
import Fruits.{Apple => _ , _}

{メンバー => _ }でそのメンバーを読み込まないって指定になるので、その後の_と組み合わせてそのメンバー以外を参照する、になるみたいですね。

inportの特殊な使い方

オブジェクトをモジュールとみなすことでimport文でアクセスしてプレフィクスの省略が出来るのね…

例えばこんな感じでアクセスしますよ

// Fruitsオブジェクトを引数にしますよ
def showFruit(fruit:Fruit) {
  // オブジェクトをモジュールにみたてます
  import fruit._
  // fruit.を省略してアクセスしマス
  println(name + "s are " + color)
}

実行結果はこのとおり

// FuitsオブジェクトのAppleを引数にしますよ
scala> showFruit(Fruits.Apple)
apples are red

うん、場所によってよさそうデスネ。28章で詳しくやるそうですー

暗黙のインポート

Scalaでは.scala拡張子付きのファイルは次のパッケージを暗黙的に読み込むみたいですねー

import java.lang._
import scala._
import.Predef._

ちなみにそれぞれこんな感じデス

  • java.lang:標準javaクラス
    • java.lang.Threadとか
  • scala:Scalaライブラリー
  • Predef:Scalaでよく使われる型やメソッド暗黙の型変換など
    • Predef.assertほか

なので上記パッケージのものはプレフィックス省略で(ThreadやList, assertなどのように)アクセスすることが出来るわけですねー

いジョー

次回はprivateとかprotectedとかのアクセス権についてやりますよー