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