2011年10月27日木曜日

Scalaのファイル入出力のサンプル

Scalaでファイルを入出力するサンプルを書いてみました。
ファイルの入力(読み込み)は専用のクラスがあるのですが、ファイルの出力に関してはなんだか適切な物が無いようです。
なので、ファイルの出力に関してはJavaのクラスを使っています。

ファイルを新規作成して文字列を出力(すでに存在する場合は追記)
その後、作成したファイルを読み込んでいます。
/**
 * Scalaでファイルの入出力
 * Javaのクラスを使ってファイルを作成し、Scalaのクラスを使ってファイルを読み込む
 */
object FileIO {
    def main( args:Array[String] ) {

        // JavaのFileOutputStreamクラスとOutputStreamWriterを別名でimport
        // それぞれインスタンス生成時の1回のみの使用なので別名にする意味は特にないです。
        import java.io.{ FileOutputStream=>FileStream, OutputStreamWriter=>StreamWriter };

        val fileName = "test.txt"
        val encode = "UTF-8"
        val append = true
        
        // 書き込み処理
        val fileOutPutStream = new FileStream(fileName, append)
        val writer = new StreamWriter( fileOutPutStream, encode )

        writer.write("あいうえお\r\n")
        writer.write("かきくけこ\r\n")
        writer.write("さしすせそ\r\n")
        writer.close


        // 読み込み処理(コンソールに出力)
        // 1行で書くとこんな感じ
        // scala.io.Source.fromFile(fileName, encode).getLines.foreach{ println _ }
        val source = scala.io.Source.fromFile(fileName, encode)
        val lines = source.getLines
        lines.foreach{ println _ }


    }
}

2011年10月26日水曜日

Scalaのアクセサメソッドの挙動

Scalaのアクセサメソッド(getterとsetter)について調べたことをまとめます。

下記ページをかなり参考にさせていただきました。
[Scala][Java] Scalaの統一アクセス(プロパティ構文)がなかなかイカしてる件

Scalaはvarかvalを使ってプロパティを宣言すると、自動的にアクセサメソッドを生成してくれます。
さらに、プロパティを直接参照するような書き方をすると、自動的にアクセサメソッドが実行されるようになっています。
そのため、

オブジェクト名.name で、自動生成されたゲッターが実行される
オブジェクト名.name = "abc" で自動生成されたゲッターが実行される

となります。

コンパイルした後に、javapを使って中身を覗いてみると宣言していないメソッドが生成されていることが分かります。

サンプルソース
class Hoge1 {
    var name:String = _
}

object Hoge1Runner {
    def main(args:Array[String]) {
        val obj = new Hoge1
        obj.name = "Tarou";

        // 実行結果は、「Hoge1:Tarou」となる
        println("Hoge1:" + obj.name);
    }
}
javap -private Hoge1の実行結果
  public class Hoge1 extends java.lang.Object implements scala.ScalaObject{
      private java.lang.String name;
      public java.lang.String name();
      public void name_$eq(java.lang.String);
      public Hoge1();
  }

自動的に生成されたアクセサメソッド使ってるって言ってもこれじゃJavaのpublicなプロパティと一緒じゃん。
値のチェックとか必要な場合は結局アクセサメソッド自前で作らなきゃいけないんでしょ?オーバライドできるの?
ということでアクセサをオーバライドしてみました。(オーバライドって言い方が正しいかどうかは分かりませんが)
getterでは、先頭に「Mr.」を、setterでは末尾に「.」を付与するようにしました。

Hoge1Runnerのrun()と、Hoge2Runerのrun()を見比べてみてください。呼び出し側の実行方法は全く変わっていません。
つまり、サクっと開発しておいて、後々呼び出されるクラスの方を修正してしまえば、呼び出し側を変更する必要が無いのです。
Javaだとこうはいきません。オブジェクト名.プロパティ名と書いたコードが山ほどあった場合、呼び出されているクラスでアクセサメソッドを追加した際、呼び出しているプログラムを全て修正する必要があります。
IDEを使っていればアクセサメソッドを自動生成してくれたり、一括置換など便利な機能があるので、特に困ることは無いかもしれませんが、ちょっとしたツールを作りたいという時にはこう言った機能が真価を発揮すると思います。

問題点として、以下のjavap実行結果を見て分かる通り、自動生成されたgetterであるname_()が定義されています。
ということは、実用上問題はないでしょうが、Hoge2#name_ = "aaa" などが実行されると意図したgetterが利用されないということになります。

サンプルソース
class Hoge2 {
    var name_ :String = _

    // getter
    def name  = {
        "Mr." + name_
    }

    // setter
    def name_= (value:String) = {
        name_ = value + "."
    }
}

object Hoge2Runner {
    def main(args:Array[String]) {
        val obj = new Hoge2
        obj.name = "Tarou";

        // 実行結果は「Hoge2:Mr.Tarou.」となる
        println("Hoge2:" + obj.name);

        // 以下の方法だと、用意したgetterが使われないので、実行結果は「Hoge2:Tarou」となります
        //(用意したsetterを使ってないのでピリオドがつかない)
        // val obj2 = new Hoge2
        // obj2.name_ = "Tarou"
        // println("Hoge2:" + obj2.name);
    }
}
javap -private Hoge2の実行結果
public class Hoge2 extends java.lang.Object implements scala.ScalaObject{
    private java.lang.String name_;
    public java.lang.String name_();
    public void name__$eq(java.lang.String);
    public java.lang.String name();
    public void name_$eq(java.lang.String);
    public Hoge2();
}

Hoge2の、意図しないgetterが利用できる問題を解決するためには、プロパティ自体をprivateにする必要があります。
プロパティをprivateにすると、自動生成されるgetterとsetterもprivateになります。
そのため、自動生成されたアクセサには、外部からアクセスでできなくなるため、意図しないアクセサ(自動生成されたアクセサ)が利用できてしまう問題を解消できます。
ただ、いきなりこの形で記述してしまうとScalaでプロパティが自動生成されるメリットが無くなってしまいます。
まずはpublicでプロパティを作っておいて、問題が出たりするタイミングで修正した方が良いかも。
setter自体を用意しなければ外部からの更新を制御することもできます。

サンプルソース
class Hoge3 {
    private var name_ :String = _

    // getter
    def name  = {
        "Mr." + name_
    }

    // setter
    def name_= (value:String) = {
        name_ = value + "."
    }
}

object Hoge3Runner {
    def main(args:Array[String]) {
        val obj = new Hoge3
        obj.name = "Tarou";

        // 実行結果は「Hoge3:Mr.Tarou.」となる
        println("Hoge3:" + obj.name);
        // 以下の方法だと、name_はprivateなのでコンパイルエラー
        // (error: variable name_ in class Hoge3 cannot be accessed in Hoge3)

        // val obj2 = new Hoge3
        // obj2.name_ = "Tarou"
        // println("Hoge3:" + obj2.name);
    }
}

javap -private Hoge3の実行結果
public class Hoge3 extends java.lang.Object implements scala.ScalaObject{
    private java.lang.String name_;
    private java.lang.String name_();
    private void name__$eq(java.lang.String);
    public java.lang.String name();
    public void name_$eq(java.lang.String);
    public Hoge3();
}

Scalaではプロパティのアクセサは自動生成されるなら、そのアクセサはどうやって修正すればいいんだ?と思って調べた結果をまとめました。
間違っている点などあればご指摘頂頂けるとありがたいです。
ちなみに、プロパティをvalで宣言すると、Javaのfinalになり、setterは自動生成されず、getterのみ自動生成されます。
Javaのfinalなので、同じクラス内からも当然値を変更できなくなります。
なんでプロパティ名にアンダーバーが必要なの?というのはまだ理解できていません。
アクセサ自体の書き方も、普通の関数とは違うようなのでまだまだ勉強しなければという感じです。

2011年10月6日木曜日

Eclipse3.7にGoogle Plugin for Eclipseをインストールしようとするとエラーが発生する

Eclipse3.7(Indigo)にGoogle Plugin for Eclipseをインストールしようとすると下記のエラーが発生しました。

1 つ以上の必須項目が見つからないため、インストールを完了できません。 Software being installed: Google App Engine Tools for Android 2.4.1.r37v201109211906 (com.google.gdt.eclipse.mobile.android.feature.feature.group 2.4.1.r37v201109211906) Missing requirement: Android Cloud Tooling 2.4.1.r37v201109211906 (com.google.gdt.eclipse.mobile.android 2.4.1.r37v201109211906) requires 'bundle com.android.ide.eclipse.adt 12.0.0' but it could not be found Cannot satisfy dependency: From: Google App Engine Tools for Android 2.4.1.r37v201109211906 (com.google.gdt.eclipse.mobile.android.feature.feature.group 2.4.1.r37v201109211906) To: com.google.gdt.eclipse.mobile.android [2.4.1.r37v201109211906]

なんかAndroid用のプラグインの依存関係が解決できないよ、というようなエラー。

これは、最初の「使用可能なソフトウェア」のところで、Google App Engine Tools for Android (requires ADT)のチェックを外して上げると解決します。
Android使わないし!という人はこれでも問題ないかと。

2011年10月5日水曜日

ScalaでHashtable系を使ってみる

object Hoge {
    def main(args:Array[String]) {
        import java.util.Hashtable

        // ----------------------------
        // 一番シンプルなパターン
        // ----------------------------
        // Javaだと Hashtable <String,String> hTable = new Hashtable <String,String>();
        val hTable = new Hashtable[String,String]()
        hTable.put("key1", 1.toString)
        hTable.put("key2", 2.toString)

        println(hTable.get("key1"))
        println(hTable.get("key2"))

        println("--------")

        // ----------------------------
        // リストに突っ込んでみる
        // ----------------------------
        //var hTableList:List[Hashtable[String, String]] = List()
        var hTableList = List[Hashtable[String, String]]()
        for ( i <- 1 to 10 ) {
            val hTable = new Hashtable[String,String]()
            hTable.put( "key", i.toString )
            hTableList = hTableList ::: List(hTable)
        }
        hTableList.map( record => println( record.get("key") ) )

        println("--------")

        // ----------------------------
        // せっかくなのでScalaのMapを使ってみる
        // ----------------------------
        //var tableList:List[Map[String,String]] = List()
        var tableList = List[Map[String,String]]()
        for ( i <- 1 to 10) {
            tableList = tableList ::: List( Map("key" -> i.toString) )
        }
        tableList.map( record => println( record.get("key") ) )

        println("--------")

        // ----------------------------
        // リストをもう少しかっこよくする
        // ----------------------------
        val listBuffer = new scala.collection.mutable.ListBuffer[Map[String,String]]
        for ( i <- 1 to 10 ) {
            listBuffer += Map( "key" -> i.toString )
        }
        listBuffer.toList.map( record => println(record.get("key")) )
    }
}