dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

[Salesforce] Apexのプロパティの挙動がよくわかってなかったのでメモ

これの話です。
Salesforce Developers

基本的な使い方はわかっていて、
ある変数に対するgetter/setter(アクセサメソッドと呼べばいいのかな)を定義すると
呼び出し側からは

// TestClassというクラスにmyPropertyというプロパティを定義した場合

TestClass test1 = new TestClass();
// getterが呼ばれる
String str = test1.myProperty;

// setterが呼ばれる
test1.myProperty = 'Hi!';

というように(クラス名).(フィールド名)で簡単にアクセスできるようになる、という話。

今回よくわからなかったのが
そのgetter/setter内で同じプロパティ名を参照しているケースで、

public class TestClass {
    public String myProperty {
        get {
            // こんなん
            if (myProperty == null) {
               // (略)
        }
    }
}

内部でどう動いているのか確認のために
次のようなコードを実行してみました。
(開発者コンソールにコピペすれば確認できます)

public class TestClass {
    public String myProperty {
        get {
            // ここのmyPropertyで何が参照されているのかよくわからなかった
            if (myProperty == null) {
                System.debug(LoggingLevel.INFO, 'myPropertyを生成します');
                myProperty = 'Hello, World!';
            }
            return myProperty;
        }
        set;
    }
}

// main
TestClass testClass = new TestClass();
System.debug(LoggingLevel.INFO, '-----EXECUTE Getter-----');
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

System.debug(LoggingLevel.INFO, '-----EXECUTE Setter-----');
testClass.myProperty = 'こんにちは!';
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

System.debug(LoggingLevel.INFO, '-----EXECUTE Setter 2(set NULL)-----');
testClass.myProperty = null;
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

以下、実行結果(USER_DEBUGのみ)

13:04:08.053 (53542047)|USER_DEBUG|[21]|INFO|-----EXECUTE Getter-----
13:04:08.053 (53637540)|USER_DEBUG|[10]|INFO|myPropertyを生成します
13:04:08.053 (53701389)|USER_DEBUG|[22]|INFO|Hello, World!
13:04:08.053 (53740810)|USER_DEBUG|[23]|INFO|Hello, World!
13:04:08.053 (53757116)|USER_DEBUG|[25]|INFO|-----EXECUTE Setter-----
13:04:08.053 (53814142)|USER_DEBUG|[27]|INFO|こんにちは!
13:04:08.053 (53848413)|USER_DEBUG|[28]|INFO|こんにちは!
13:04:08.053 (53864121)|USER_DEBUG|[30]|INFO|-----EXECUTE Setter 2(set NULL)-----
13:04:08.053 (53917113)|USER_DEBUG|[10]|INFO|myPropertyを生成します
13:04:08.053 (53939613)|USER_DEBUG|[32]|INFO|Hello, World!
13:04:08.053 (53973310)|USER_DEBUG|[33]|INFO|Hello, World!

あーなるほど。
if文のmyPropertyは自分自身を指していて、
初回だけ値が定義されていない(null)からif文の中身が実行されますよ、ということなんですね。

ただし、この書き方だとmyPropertyにnullをセットすることができなくなりますよと。
(セットできるけど、getterが呼び出された時に初期化される)








(追記)
せっかくなのでもう少し試してみます。
検証1:getter内でmyPropertyに値をセットしなかった場合

public class TestClass {
    public String myProperty {
        get {
            String str;
            if (myProperty == null) {
                System.debug(LoggingLevel.INFO, 'myPropertyを生成します');
                // ****変更点:別の変数に格納してみる****
                str = 'Hello, World!';
            }
            return str;
        }
        set;
    }
}

// main
TestClass testClass = new TestClass();
System.debug(LoggingLevel.INFO, '-----EXECUTE Getter-----');
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

System.debug(LoggingLevel.INFO, '-----EXECUTE Setter-----');
testClass.myProperty = 'こんにちは!';
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

System.debug(LoggingLevel.INFO, '-----EXECUTE Setter 2(set NULL)-----');
testClass.myProperty = null;
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

結果1

14:23:10.076 (76091749)|USER_DEBUG|[23]|INFO|-----EXECUTE Getter-----
14:23:10.076 (76207524)|USER_DEBUG|[10]|INFO|myPropertyを生成します
14:23:10.076 (76235960)|USER_DEBUG|[24]|INFO|Hello, World!
14:23:10.076 (76273414)|USER_DEBUG|[10]|INFO|myPropertyを生成します
14:23:10.076 (76290322)|USER_DEBUG|[25]|INFO|Hello, World!
14:23:10.076 (76308709)|USER_DEBUG|[27]|INFO|-----EXECUTE Setter-----
14:23:10.076 (76392745)|USER_DEBUG|[29]|INFO|null
14:23:10.076 (76429525)|USER_DEBUG|[30]|INFO|null
14:23:10.076 (76446691)|USER_DEBUG|[32]|INFO|-----EXECUTE Setter 2(set NULL)-----
14:23:10.076 (76501997)|USER_DEBUG|[10]|INFO|myPropertyを生成します
14:23:10.076 (76517669)|USER_DEBUG|[34]|INFO|Hello, World!
14:23:10.076 (76552548)|USER_DEBUG|[10]|INFO|myPropertyを生成します
14:23:10.076 (76569634)|USER_DEBUG|[35]|INFO|Hello, World!

myPropertyは毎回nullとして扱われていますね。


検証2:setterがなかったら?

public class TestClass {
    public String myProperty {
        get {
            String str;
            if (myProperty == null) {
                System.debug(LoggingLevel.INFO, 'myPropertyを生成します');
                myProperty = 'Hello, World!';
            }
            return str;
        }
        // set;
    }
}

// main
TestClass testClass = new TestClass();
System.debug(LoggingLevel.INFO, '-----EXECUTE Getter-----');
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

System.debug(LoggingLevel.INFO, '-----EXECUTE Setter-----');
testClass.myProperty = 'こんにちは!';
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

System.debug(LoggingLevel.INFO, '-----EXECUTE Setter 2(set NULL)-----');
testClass.myProperty = null;
System.debug(LoggingLevel.INFO, testClass.myProperty);
System.debug(LoggingLevel.INFO, testClass.myProperty);

結果2

ERROR: member variable not visible for assignment

エラーになりました。

検証1、2についてはともに期待した通りでした。
getter内でmyProperty = してるところってsetter呼んでるんだな、という。