【Apex】String.format()を広めたい

# 導入

文字列連結に便利な`String.format()`の紹介です。
こちらのサンプルコードをご覧ください。

```apex
String accountName = '田中 太郎';
String changedFieldLabel = '電話';
String oldValue = '09012345678';
String newValue = '09099999999';

String result = '取引先： ' + accountName + 'の「' + changedFieldLabel + '」が' + oldValue + 'から' + newValue + 'に変更されました。';
System.debug(result);

// 取引先： 田中 太郎の「電話」が09012345678から09099999999に変更されました。
```

よくある文字列連結です。
JavaScriptのテンプレートリテラルのような記法が存在しないため、Apexでは無理矢理文字列を足し合わせていくしかありません。

上記を`String.format()`で書き直すと以下の通りです。

```apex
String template = '取引先： {0}の「{1}」が{2}から{3}に変更されました。';
String accountName = '田中 太郎';
String changedFieldLabel = '電話';
String oldValue = '09012345678';
String newValue = '09099999999';

List<String> insertList = new List<String>();
insertList.add(accountName);
insertList.add(changedFieldLabel);
insertList.add(oldValue);
insertList.add(newValue);

String result = String.format(template, insertList);
System.debug(result);

// 取引先： 田中 太郎の「電話」が09012345678から09099999999に変更されました。
```

行数は増えましたが、可読性が上がりすっきりまとまったように感じます！

# 本題

改めて、本記事では[Stringクラスのfomrat関数](https://developer.salesforce.com/docs/atlas.ja-jp.234.0.apexcode.meta/apexcode/apex_methods_system_string.htm#apex_System_String_format)について紹介します。

## 使い方

ドキュメントの内容を掻い摘んで説明します。定義は以下の通りです。
`public static String format(String stringToFormat, List<Object> formattingArguments)`

### ・stringToFormat  
導入例文では`template`に代入している文字列、`'取引先： {0}の「{1}」が{2}から{3}に変更されました。'`の部分です。
formattingArgumentsで置き換える位置を`{添字}`で指定します。

### ・formattingArguments  
導入例文の`insertList`です。
stringToFormat内の`{添字}`に代入したい順番で要素を追加しておきます。

## ポイント
### 【1】formattingArgumentsの要素数が足りなくてもOK

```apex
String template = '0 → {0} / 1 → {1} / 2 → {2} / 3 → {3} / 4 → {4}';
List<String> insertList = new List<String>();
insertList.add('0番目');
insertList.add('1番目');
insertList.add('2番目');
String result = String.format(template, insertList);
System.debug(result);

// 0 → 0番目 / 1 → 1番目 / 2 → 2番目 / 3 → {3} / 4 → {4}
```

直感的にはエラーが発生しそうですが、上記コードはエラーになりません。
ご覧の通り`{添字}`の部分がそのまま残るだけです。

### 【2】formattingArguments要素数が多くてもOK

```apex
String template = '0 → {0} / 1 → {1} / 2 → {2} / 3 → {3} / 4 → {4}';
List<String> insertList = new List<String>();
insertList.add('0番目');
insertList.add('1番目');
insertList.add('2番目');
insertList.add('3番目');
insertList.add('4番目');
insertList.add('5番目');
insertList.add('6番目');
insertList.add('7番目');
String result = String.format(template, insertList);
System.debug(result);

// 0 → 0番目 / 1 → 1番目 / 2 → 2番目 / 3 → 3番目 / 4 → 4番目
```

こちらも超過分が単に無視されるだけでエラーにはなりません。

# 使用例
## 【1】エラーメッセージ整形

カスタム表示ラベルを以下の通り作成しておき、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/b64cbe3f-fa43-a310-a3b9-8a27d823555b.png)

catch句などでメッセージを整形（今回は画面に表示するケースを想定）


```apex
try {
    // ListExceptionを発生させる
    List<Account> accountList = new List<Account>();
    System.debug(accountList[0].Name);
} catch (Exception e) {
    List<String> insertList = new List<String>();
    insertList.add(e.getTypeName());
    insertList.add(e.getMessage());
    insertList.add(e.getStackTraceString());
    String message = String.format(System.Label.ERROR_MSG_MESSAGE_FORMAT, insertList);
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, message));
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573865/308e3a40-3cae-7fd1-5908-d1b78ab147b7.png)

~~この若干の日本語化が必要かどうかは怪しいですが~~エラーをただ垂れ流すよりは、しっかり例外処理してる感が出てますね。
`String.format()`を使わない場合はかなり冗長な記述になるので、文字列の加工としては中々使い勝手が良さそうです。

## 【2】Database.query()での使用

動的SOQL実行用queryを作成する関数を雑に作ってみました。

```apex
public with sharing class DynamicQueryCreator {
    private static final String BASE_QUERY = 'SELECT {1} FROM {0}';
    private static final String WHERE_CLAUSE = ' WHERE ';
    private static final String ORDERBY_CLAUSE = ' ORDER BY ';

    public static String getQueryString(String objectName, String fields, List<String> conditionList, List<String> orderbyList){
        List<String> insertList = new List<String>();
        insertList.add(objectName);
        insertList.add(fields);

        String query = String.format(BASE_QUERY, insertList);

        if(conditionList != null && !conditionList.isEmpty()){
            query += WHERE_CLAUSE + String.join(conditionList, ' AND ');
        }

        if(orderbyList != null && !orderbyList.isEmpty()){
            query += ORDERBY_CLAUSE + String.join(orderbyList, ',');
        }

        return query;
    }
}
```

以下使用例です。

```apex
String objectName = 'Account';
String fields = 'Id, Name, Phone';
List<String> conditionList = new List<String>{'Name = \'田中 太郎\'', 'IsDeleted = false'};
List<String> orderbyList = new List<String>{'Name, Id'};

String query = DynamicQueryCreator.getQueryString(objectName, fields, conditionList, orderbyList);
System.debug(query);
List<Account> accountList = Database.query(query);

// SELECT Id, Name, Phone FROM Account WHERE Name = '田中 太郎' AND IsDeleted = false ORDER BY Name, Id
```

はい、これは正直微妙な気がしますね。
初めは結構良いアイデアだと思って作り始め、途中で違和感を感じつつ作りきってみましたが案の定でした。
~~可読性に気を使ってる感は出せたと思うので許してください~~

# おわりに

使用例は若干微妙でしたが、`String.format()`の有用性だけでも分かってもらえれば幸いです。
スマートな？文字列連結をぜひ試してみてください！