LL脳がscalaの勉強を始めたよ その101
Scalaコップ本の29章に入っていきますよ(`・ω・´)29章はScalaとJavaの結合についてやっていきますねー
Scalaの特徴の1つとしてJavaとの親和性が挙げられることが多いですが、この説ではソレにまつわるエトセトラについてやっていきますデス
JavaからScalaを使うための注意点
まずは、Javaから見てScalaコードがどのように見えるのかを見ていきますデス(`・ω・´)
一般原則
基本的にScalaコードはコンパイラによって標準Javaバイトコードに翻訳されるみたいです。このように、Javaとの親和性を確保するようにScalaの設計は行われているのですが、いくつかJavaとは異なる独自設計を選んだ昨日もあるそうです。
ScalaとJavaとが異なるものとしては、例えばこんなものがありますな
上記のようにそのままScalaコードをJava機能にマッピングできないモノについてはJavaの持っている構造を組み合わせることでJavaバイトコードへの変換を行っているみたいです。しかしながら変換方法については固定されておらず、Scalaのバージョンアップと共に最適なものを模索しているとのことです。なお、現在のScalaコンパイラーの方法はjavapコマンドでclassファイルを解析することで探索することが可能とのことです。
値型
値型については次の2通りの方法でJavaへの翻訳が行われているみたいです。
コンパイラは通常は前者の方法を使うことでパフォーマンスを確保するらしいのですが、例えばList[Any]等のようにInt以外を扱うかも知れない場合のようにコンパイラーが確証を得られない場合は後者を使うみたいです。
後者になってしまうと若干パフォーマンスが落ちてしまうみたいですねぇ(´・ω・`)
シングルトンオブジェクト
Javaはシングルトンオブジェクトを持っていないものの、似たようなものとして静的メソッドをもっているようなので、ScalaのシングルトンオブジェクトとJavaの静的メソッドとインスタンスメソッドの組み合わせで翻訳されるみたいです。
すべてのScalaシングルトンオブジェクト(例えばAppオブジェクト)はコンパイラーによってドル記号が末尾につけられたJavaクラス(App$クラス)に変換されるみたいです。このApp$クラスではAppオブジェクトの持っている全てのメソッドとフィールドを持っていて、かつ実行時に作成される唯一のクラスインスタンスを保持するためのMODULE$という名前の静的フィールドも持っているそうです。
変換例はこんな感じになりますね。Appシングルトンオブジェクトをこんなふうに定義してみますよ
object App { def main(args:Array[String]){ println("Hello, Java World!") } }
上記をコンパイラが翻訳してできたApp$は次のようになるみたいです(javapでApp$を眺めた結果です)
public final class App$ extends java.lang.Object implements scala.ScalaObject { public static final App$ MODULE$; public static {}; public App$(); public void main(java.lang.String[]); public int $tag(); }
...Javaコードあんまりわかんね(´・ω・`)と思いつつ、雰囲気だけはなんとか‥.ちなみにScalaのスタンドアロンシングルトンオブジェクトのように同名のクラスのないシングルトンオブジェクトを翻訳する場合、コンパイラは同名にJavaクラスを作ってシングルトンオブジェクトの各メソッドから呼び出すための転送用静的メソッドを定義するみたいです。
たとえば上記Appオブジェクトがスタンドアロンシングルトンオブジェクトだった場合は、次のようなJavaクラスが生成されるみたいです(javapでAppを眺めた結果です)
Compiled from "App.scala" public final class App extends java.lang.Object { // App$からの呼び出し転送用の定義ですね public static final int $tag(); public static final void main(java.lang.String[]); }
なお、スタンドアロンシングルトンオブジェクトと同名のクラスがあった場合はコンパイラが上記のようなJavaの同名クラスの生成は行わないため、Javaコードは静的フィールドのMODULE$を介してシングルトン(APP$)にアクセスする必要があるみたいです。
…(´ε`;)ウーン…シングルトンオブジェクト周りは結構複雑ですな。スタンドアロンの方が扱いやすいのかしら?
アノテーション
ScalaのアノテーションでJavaに関わる部分のみを取り上げてみていきますねー
標準アノテーションの特別な効果
Javaプラットフォームをターゲットとしたいくつかのアノテーションを使うと、コンパイラーがJava用の特別な情報を生成するみたいです。コンパイラがアノテーションを処理する順序としては次のようになるみたいです。
Javaと連携する標準アノテーションとしては次のようなものがありますね
- 使うべきではない機能(@deprecated)
- 揮発性フィールド(@volatile)
- シリアライゼーション(@serializable, @SerialVersionUID(1234L), @transient)
...と上記のような追加情報が生成されるようです。Javaの世界の人と会話する際に使えそうなので頑張って覚えたいと思います(`・ω・´)
投げられた例外
ScalaにはJavaのメソッドに対するthrows宣言にあたるものがないので、投げられた例外がキャッチされたことをチェックしないそうです。なので全てのScalaメソッドは例外の宣言がないJavaメソッドに変換されるとのことです。これはJavaの例外処理コードを書く作業があまりにも負担が大きいため、適切に運用されていない(適当に例外処理されている)→だったら省略すればいいじゃん(乱暴な意訳)という流れのようです。
ただし、Javaとの併用をきちんと行うためにはメソッドが投げうる例外への対処を行わないと行けない場面がでてくるので、Scalaでは@throwsアノテーションでこれに対応することにしているようです。@throwsアノテーションは例えば次のように使用するみたいデスね(´・ω・`)次のサンプルではRMIリモートインターフェースに含まれるメソッドに対応するためにRemoteException例外をリストアップしているそうです。
import java.io._ class Reader(fname:String){ private val in = new BufferedReader(new FileReader(frname)) // throwsアノテーションでIOExceptionをリストアップします @throws(classOf[IOException]) def read() = in.read() }
上記のコードはJavaから見ると次のように見えるみたいです
public class Reader extends java.lang.Object implements scala.ScalaObject{ public Reader(java.lang.String); // IOExceptionを投げるかも、宣言をしていますね public int read() throws java.io.IOException; public int $tag(); }
Javaアノテーション
Javaフレームワークの既存アノテーションはScalaコードから直接使うことができるみたいです。コンパイルされたScalaコードをJavaフレームワークから見ると、それらのアノテーションはJavaで書いたのと同じように見えるそうです。
実際にJavaフレームワークのアノテーションをScalaで使うサンプルとしてJUnit4を扱ってみます。自動テストを書いて実行するJUnit4ではアノテーションを使ってコードのどの部分がテストなのかを示すそうです。
とりあえずサンプルを書いてみますよ、テスト部分には次のようにアノテーションを付けるだけですね
import org.junit.Test import org.junit.Assert.assertEquals class SetTest { @Test def testMultiAdd { val set = Set() + 1 + 2 + 3 + 1 + 2 + 3 // setのサイズが3の場合はOKです asserEquals(3, set.size) } }
junitのテストは本来はorg.junit.Testアノテーションで表現されるらしいのですが、上記コードではorg.junit.Testをインポートしているので単純に@Testアノテーションで表現しております。
ではjunitを実際に実行してみますよ
// コンパイルします % scalac -cp junit/junit-4.8.2.jar SetTest.scala % scala -cp junit/junit-4.8.2.jar:. org.junit.runner.JUnitCore SetTest JUnit version 4.8.2 . Time: 0.012 OK (1 test)
おお、無事テストが行われました(´・ω・`)以前Java関連のテストフレームワークが上手く動かなかったのでチョット不安でしたがうまくいってよかった…