2011年9月29日木曜日

Vimテクニカルバイブルを買ったよ。

Vimユーザなので購入。


emacs版もあるらしい。emacs使えないから買うことはないけど。

2011年9月25日日曜日

[Scala]アクセッサの定義

scalaではとあるルールによってアクスメソッド(getterやsetter)が自動的に定義されるので、
Javaみたく明示的にアクセスメソッドを定義する必要はないようです。素敵ですね。


パターン1:publicなvarフィールドを定義した場合

このように、publicなvarフィールドを定義するとコンパイル時に自動的にアクセスメソッドを出力してくれます。
class Hoge {

  var hoge1: String = _

  var hoge2: Int = _
}
自動で生成されたアクセスメソッドを呼び出すには、フィールドに直接アクセスするようにしてあげればよいです。
val hoge = new Hoge
hoge.hoge1 = "hoge"     // hoge1に値を設定
hoge.hoge2 = 1             // hoge2に値を設定

println(hoge.hoge1)        // hoge1の値を取得
println(hoge.hoge2)        // hoge2の値を取得

コンパイル後に出力されたクラスファイルをjadって見てみると、こんなルールになっているのがわかる。
  • varなフィールドはprivateになっている。
  • setterは、プロパティ名 + "_="になっている。
    =は、コンパイル時にJavaで認識できる文字に置き換えられるので「$eq」となっている。
  • getterは、プロパティ名になっている。
//***********************************************
// Hogeクラス
//***********************************************
public class Hoge 
    implements ScalaObject 
{ 
    // hoge1のgetter
    public String hoge1() 
    { 
        return hoge1; 
    } 
    // hoge1のsetter
    public void hoge1_$eq(String s) 
    { 
        hoge1 = s; 
    } 
    // hoge2のgetter
    public int hoge2() 
    { 
        return hoge2; 
    } 
    // hoge2のsetter
    public void hoge2_$eq(int i) 
    { 
        hoge2 = i; 
    } 
 
    public Hoge() 
    { 
    } 
 
    // 公開フィールドは、privateなフィールドとなっている。
    private String hoge1; 
    private int hoge2; 
} 

//***********************************************
// アクセスメソッドを呼び出すコード
//***********************************************
Hoge hoge = new Hoge();
// setter(プロパティ名 + "_=")を呼び出すコードに変換されているのがわかる。
hoge.hoge1_$eq("hoge");
hoge.hoge2_$eq(1);

Predef$.MODULE$.println(hoge.hoge1());
Predef$.MODULE$.println(BoxesRunTime.boxToInteger(hoge.hoge2()));
ちなみち、varフィールドをprivateにするとアクセスメソッドが生成されないのでこんな感じにコンパイルエラーが出る。
    Error:Error:line (6)error: variable hoge1 in class Hoge cannot be accessed in Hoge
hoge.hoge1 = "hoge"
    Error:Error:line (7)error: variable hoge2 in class Hoge cannot be accessed in Hoge
hoge.hoge2 = 1
    Error:Error:line (9)error: variable hoge1 in class Hoge cannot be accessed in Hoge
println(hoge.hoge1)
    Error:Error:line (10)error: variable hoge2 in class Hoge cannot be accessed in Hoge
println(hoge.hoge2)

パターン2:明示的にアクセスメソッドを定義する方法

明示的にアクセスメソッドを定義するには、フィールドをprivateにしてあげてパターン1で自動生成された形式でsetterとgetterを宣言してあげればよいです。
例えば、アクセスメソッドで処理をしたい場合やgetterのみを宣言する場合に使います。(Javaのように、getHogeやsetHogeを宣言する必要はないです。)
気をつけないといけないのは、フィールド名とgetter名が同じになってはいけないのと、
setter名は、プロパティ名の後に「_=」をつけるのを忘れないこと。「_」と「=」の間にスペース入れちゃうのもダメ。
  // フィールドはprivateで宣言する。
  private var h1: String = _
  private var h2: Int = _

  // getter
  def hoge1: String = h1

  // setter
  def hoge2_=(hoge2: Int) {
    require(hoge2 > 100)
    h2 = hoge2;
  }
  // getter
  def hoge2: Int = h2

2011年9月17日土曜日

[scala]Listの色々

Listの操作のまとめ。

Listの生成

    // listの生成
    val list = 1::2::Nil            // Nilは、終端を示す
    val list2 = List("1", "2", "3")

    // 空のlistの生成
    val emp = Nil

パターンマッチでの分解

      val list = List(1, 2, 3)

      // Listの各要素を変数に分解
      // Listの要素数と、展開先の要素数が一致している必要がある。
      val List(n1, n2, n3) = list
      println("n1 = " + n1)    // 1
      println("n2 = " + n2)    // 2
      println("n3 = " + n3)    // 3

      // Listの要素が不確定の場合は、::(コンスと呼ぶらしい)を使って展開する。
      // この例だと1つ目の要素がnに代入され、残りの要素がListとしてnxに代入される。
      val n::nx = list
      println(n)                      // 1
      println("nx = " + nx)      // List(2, 3)

      // caseをつかったパターンマッチ
      list match {
        case List() => println("空のList")
        case n::nx => {
          println(n)           // 1
          println(nx)         // List(2, 3)
        }
      }

Listの結合

      val list = List(1, 2, 3)

      // :::を使ってList同士を結合出来る。
      val list2 = list ::: 1 :: 2 :: 3 :: Nil
      println("list2 = " + list2)    // List(1, 2, 3, 1, 2, 3)

Listのサイズ

Listの要素数は、lengthメソッドで取得出来ます。
      val list = List(1, 2, 3)

      list.length   // 3

lengthメソッドは要素数に比例してコストが高くなるらしいので要素が空か判定する場合には、length == 0ではなくisEmptyを使った方がよいそうです。
実際にListのコード見てみると要素が空になるまでtail(先頭の要素を除外したListを返すメソッド)してるのでかなりコスト高そう・・・。
  def length: Int = {
    var these = self
    var len = 0
    while (!these.isEmpty) {
      len += 1
      these = these.tail
    }
    len
  }

先頭、末尾へのアクセス

      val list = List(1, 2, 3)
      // 先頭の要素
      list.head    // 1
      // 先頭の要素を取り除いたList
      list.tail       // List(2, 3)
      // 最後の要素
      list.last      // 3
      // 最後の要素を取り除いたList
      list.init       // List(1, 2)
※最後の要素へアクセスするlastやinitはlengthと同じようにコストが高くつく。頻繁に末尾要素へアクセスする場合には、事前にreverseメソッドで要素を逆転するのがよいみたいです。

その他

      val list = List('a', 'b', 'c')
      // mapメソッド
      // 引数で指定された関数の結果から新たなListを生成する。
      // この例だと、list変数の各要素を大文字に変換したListを生成する。
      val upperList = list.map(_.toUpper)

      // Listをもとに繰り返し処理を行う場合には、foreachを使う
      // Javaでのfor文かな
      val builder = StringBuilder.newBuilder
      list.foreach(builder.append(_))
      builder     // abcとなる。

      // 条件に一致する値だけに絞り込んだListを生成する
      val abList = list.filter(c => c == 'a' || c == 'b')

      // 条件に一致した最初の要素を返す
      val n = List(1, 2, 3, 4, 5).find(_ % 2 == 0)

JDBCでORA-01000が出た場合の対処

OPEN_CURSORS初期パラメータが適切に設定されていない場合や、
JDBCを使用したアプリケーションでリソース開放を適切に行っていない場合に「ORA-01000: 最大オープン・カーソル数を超えました。」が発生する場合があります。

JDBCを使用したアプリケーションが適切にリソース開放を行えているのであれば、一つのDBセッションで発行するSQLの種類をもとにOPEN_CURSORSを設定してあげることができます。
マニュアルには、「OPEN_CURSORSに指定する値が実際に必要な数より大きくても、セッションでオープンするカーソル数が指定した値よりも小さければ、余分なオーバーヘッドはありません。」と書かれているのでかなり余裕をもった値を設定すればいいのかなとも思います。

OPEN_CURSORSを変更する方法

現在のOPEN_CURSORSの値の確認方法

sqlplusから下記コマンドを実行することにより確認できます。
show parameters open_cursors

※「SELECT ON V_$PARAMETERオブジェクト権限」が必要なので、権限をもったユーザでログインする必要があります。

OPEN_CURSORSの変更方法

例えば、1000に変更したい場
alter system set open_cursors = 1000;

上記コマンドを実行したあとのOPEN_CURSORS値
SQL> show parameters open_cursors;

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
open_cursors                         integer     1000

※「ALTER SYSTEMシステム権限」を持つユーザで実行する必要があります。

アプリケーションの実装を見直す場合

必ずリソース開放がされていることを確認する必要があります。ここで言うリソースとは、java.sql.Statementのことであってjava.sql.ResultSetでありません。
注意しないといけないのは、DML文に対応するStatementも1カーソルとしてカウントされてしまうことです。

※普通なシステム開発では、フレームワークでリソース開放してくれるはずなんでアプリでリソース開放なんてする必要はないはずです。
昔いけてないフレームワークでリソース開放してくれずにこのエラーが頻発したことありましたが・・・。

正しいリソース開放の例
        // DML文の場合
        PreparedStatement statement = connection.prepareStatement(
                "SQL文");
        try {
            statement.executeUpdate();
        } finally {
            // finallyでstatementをcloseする。
            statement.close();
        }

        // selectの場合
        PreparedStatement select = connection.prepareStatement(
                "SQL文");
        try {
            ResultSet rs = statement.executeQuery();
            while (rs.next()) {
                // 処理
            }
        } finally {
            // finallyでstatementをcloseする。
            // ResultSetは、statementクローズ時にcloseされる。
            select.close();
        }


2011年9月14日水曜日

[Intellij]Mac OS Xの場合の構成ファイルの場所

Windowsだと、IDEA_HOME/bin/idea.propertiesに記述されている内容通りに構成ファイル(pluginやlogが出力されるフォルダ)が配置されていたけど、
Mac OS Xにしてみたらidea.propertiesの設定通りの場所に構成ファイルを見つけることができず・・・。

Mac OS Xでも設定内容は、Windowsと一緒だったので普通に考えるとユーザホームはいかに「.IntellijIdea10」のディレクトリがあると思ったんだけど存在せず・・・。
頑張ってfindした結果Macでは、下記構成になっているらしい。
調べた後に気づいたんだけど、JetBrainsのサイトにOSごとの構成書いてあった。
http://devnet.jetbrains.net/docs/DOC-181

  • configディレクトリ
    ~/Library/Preferences/IntelliJIdea10
  • pluginディレクトリ
    ~/Library/Application Support/IntelliJIdea10
  • ログディレクトリ
    ~/Library/Logs/IntelliJIdea10

[JUnit]assertThat用のMatcherを作成してみる

assertThat用のMatherはデフォルトで色々用意されているけど、拡張可能になっているので用途にあわせて独自に追加可能となっている。

基本的には、タイプセーフな「org.junit.internal.matchers.TypeSafeMatcher」を継承して拡張を行えばよい。
TypeSafeMatcherを拡張するポイントは、こんな感じ。
  • matchesSafely
    期待値と実際の値の比較を行うメソッド。
    比較結果をbooleanで返却してあげる。
  • describeTo
    期待値と実際の値が異なる場合の実際のメッセージに表示する値を生成する。(下の画像の部分)

サンプル

このサンプルは、実際の値がjava.util.Dateの場合に、期待値に8桁の文字列を指定出来るようにするMatcher。
実際にisを使うと期待値と実際の値はタイプが一致していないといけないので、困難があると決行便利だったりする。

    @Test
    public void testGetDate() {
        // 実際の値はDateだが、期待値には8桁の文字列日付を指定する。
        assertThat(Hoge.getDate(), DateMatcher.isDate("20110910"));
    }

    /**
     * Date型と8桁の文字列をassertするクラス。
     * org.junit.internal.matchers.TypeSafeMatcherを継承して機能拡張する。
     */
    private static class DateMatcher extends TypeSafeMatcher {

        private String expected;
        private Date expectedDate;

        public DateMatcher(String expected) {
            this.expected = expected;
            if (expected == null) {
                return;
            }
            // 期待値が有効な日付かのチェックを事前に行う。
            SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
            try {
                expectedDate = yyyyMMdd.parse(expected);
            } catch (ParseException e) {
                throw new IllegalArgumentException("8桁の有効な日付を指定してください。", e);
            }
        }

        /**
         * assertThatの期待値に指定するMatcherのファクトリメソッド。
         * @param date 期待値(8桁の有効な日付)
         * @return Matcher
         */
        public static DateMatcher isDate(String date) {
            return new DateMatcher(date);
        }

        /**
         * メッセージの構築。
         * 期待値をDateに変換してメッセージに追加する。(時間部分は差分として出ちゃうけど、めんどいのでこの辺は適当)
         * @param description
         */
        public void describeTo(Description description) {
            if (expected == null) {
                description.appendText(expected);
            } else {
                description.appendValue(expectedDate);
            }
        }

        /**
         * 実際の値との比較を行う。
         * @param item 実際の値
         * @return 一致している場合はtrue
         */
        @Override
        public boolean matchesSafely(Date item) {
            if (item == null && expected == null)  {
                return true;
            }
            // 実際の値を8桁の文字列日付に変換して比較を行う。
            SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
            String actual = yyyyMMdd.format(item);
            return actual.equals(expected);
        }
    }

2011年9月13日火曜日

[Intellij]行番号の表示、非表示の切り替え

Intellijのコードエディタの行番号を表示する方法

ファイル単位で切り替える場合は、下の画像のように、メニューバーの「View」→「Show Line Numbers」で切り替えることができます。
※エディタがアクティブになっていないと出てくるメニュー内容が変わるのでご注意を。



すべてのファイルで常に行番号を表示させたい場合は、「settings」→「Editor」→「Appearance」の「Show Line Numbers」をオンにしてあげればよいです。


「Intellij 行番号」でのアクセスがあったので書いてみました。

Scalaで正規表現を使ったパターンマッチ

正規表現を使ったパターンマッチの例
    def patternMatch(args:String) {
      // 正規表現を定義する
      val pattern1 = "([0-9]+)".r
      val pattern2 = "([a-zA-Z]+)".r
      args match {
        // caseで定義した正規表現を指定する。
        // 前方参照可能なグループがある場合には、そのグループを代入する定数を指定する。
        case pattern1(n) => println("number = " + n)
        case pattern2(s) => println("string = " + s)
        case s:String => println("other = " + s)
      }
    }
    patternMatch("100")             // -> number = 100
    patternMatch("abc")             // -> string = abc
    patternMatch("abc100")        // -> other = abc100
こんな感じにいけるんかと思ってたけどコンパイルエラーで悩まされた・・・
      args match {
        case "([0-9])+".r => println("number")
        case "([a-zA-Z)+".r => println("string")
        case s: String => println("other = " + s)
      }

2011年9月4日日曜日

OracleのBLOBにJavaオブジェクトを保存

OracleデータベースのBLOB型のカラムにJavaオブジェクトを保存する方法。


サンプルコード

データベース定義

SQL> desc blob_test

 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL CHAR(5)
 OBJECT_DATA                                        BLOB

データベースへの保存方法

        // DBへ保存するオブジェクトを生成
        // DBへ保存するオブジェクトは直列化可能でなければならない。
        // (Serializableをimplementsする必要がある)
        HogeInfo info = new HogeInfo();
        info.setId("ID");
        info.setName("なまえ");

        // 上記で生成したオブジェクトを直列化し、OutputStreamへ書き出す。
        // この例だと、ByteArrayOutputStreamに出力をしている。
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream stream = new ObjectOutputStream(out);
        stream.writeObject(info);

        PreparedStatement preparedStatement = con.prepareStatement(
                "insert into blob_test (id, OBJECT_DATA) values (?, ?)");

        try {
            preparedStatement.setString(1, "00001");
            // ByteArrayOutputStreamに出力したオブジェクトの情報を、byte配列で取得し
            // BLOBカラムにバインドする。
            preparedStatement.setBytes(2, out.toByteArray());
            preparedStatement.executeUpdate();
        } finally {
            preparedStatement.close();
        }

データベースからの取得方法

        PreparedStatement preparedStatement = connection.prepareStatement(
                "select object_data from blob_test where id = '00001'");
        byte[] bytes;
        try {
            ResultSet resultSet = preparedStatement.executeQuery();
            resultSet.next();
            // BLOBカラムの値をbyte配列で取得
            bytes = resultSet.getBytes(1);
        } finally {
            preparedStatement.close();
        }

        // 取得したオブジェクトをObjectInputStreamを使ってデシリアライズする。
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes));
        HogeInfo hogeInfo = (HogeInfo) objectInputStream.readObject();

        // デシリアライズしたオブジェクトの属性を出力
        System.out.println("hogeInfo.getId() = " + hogeInfo.getId());
        System.out.println("hogeInfo.getName() = " + hogeInfo.getName());

実行結果

保存したオブジェクトの属性が、復元できていることが分かる。
hogeInfo.getId() = ID
hogeInfo.getName() = なまえ