1

この記事は最終更新日から3年以上が経過しています。

リテラルは必ず定数にしてほしい

投稿日

今回はリテラルの扱い方について書いていこうと思います。
リテラルは初学者から扱うことが極めて多いものです。

教科書の Hello World でいきなり使います。

ですが、こいつの扱いがザツなことが多いですね。

リテラルは出現回数が1回でも定数にせよ

リテラルとは次の "bust" などのことを言います。

悪い例

sample.java
/** データベースから name のスリーサイズをmapで返却 */
public static Map<String, Integer> getThreeSize(String name){
    ThreeSizeDto threeSizeDto = threeSizeDb.getThreeSize(name);
    Map<String, Integer> retValue = new HashMap<>();
    retValue.put("bust", threeSizeDto.getBust());
    retValue.put("west", threeSizeDto.getWest());
    retValue.put("hip", threeSizeDto.getHip());
    return retValue;
}

/** スリーサイズのmapを生成 */
public static Map<String, Integer> setThreeSize(Integer bust, Integer west, Integer hip){
    Map<String, Integer> retValue = new HashMap<>();
    retValue.put("bust", bust);
    retValue.put("west", threeSizeDto.getWest());
    retValue.put("hip", threeSizeDto.getHip());
    return retValue;
}

public static void main(String[] args){
    Map<String, Integer> threeSizeMap = getThreeSize(args[0]);
    System.out.println(args[0] + "さんのバストサイズは" + threeSizeMap.get("bust"));
}

これセットする側も参照側も同じこと書いてますよね。

人間がプログラムを追ってみれば、"bust"とは、threeSizeDto.getBust() を表しているとわかりますが、追わないとわからないんですよ。
追わせないでください。 時間の無駄です。

これを定数にすることで、定数から意味を知ることができます。

良い例

sample.java
/** 定数 */
public class Constants {
    /** スリーサイズのバスト */
    public static final String KEY_THREE_SIZE_BUST = "bust";
    /** スリーサイズのウエスト */
    public static final String KEY_THREE_SIZE_WEST = "west";
    /** スリーサイズのヒップ */
    public static final String KEY_THREE_SIZE_HIP = "hip";
}

/** データベースから name のスリーサイズをmapで返却 */
public static Map<String, Integer> getThreeSize(String name){
    ThreeSizeDto threeSizeDto = threeSizeDb.getThreeSize(name);
    Map<String, Integer> retValue = new HashMap<>();
    retValue.put(KEY_THREE_SIZE_BUST , threeSizeDto.getBust());
    retValue.put(KEY_THREE_SIZE_WEST , threeSizeDto.getWest());
    retValue.put(KEY_THREE_SIZE_HIP , threeSizeDto.getHip());
    return retValue;
}

/** スリーサイズのmapを生成 */
public static Map<String, Integer> setThreeSize(Integer bust, Integer west, Integer hip){
    Map<String, Integer> retValue = new HashMap<>();
    retValue.put(KEY_THREE_SIZE_BUST , bust);
    retValue.put(KEY_THREE_SIZE_WEST , threeSizeDto.getWest());
    retValue.put(KEY_THREE_SIZE_HIP , threeSizeDto.getHip());
    return retValue;
}

public static void main(String[] args){
    Map<String, Integer> threeSizeMap = getThreeSize(args[0]);
    System.out.println(args[0] + "さんのバストサイズは" + threeSizeMap.get(KEY_THREE_SIZE_BUST ));
}

こうすることで、リテラルの直記載では得られなかった 「意味のつながり」 が表現されます。
getThreeSize と setThreeSize で、定数を共有しているため、全く同じ意味の map であることが表現されます。

※Javaをちゃんとやっている人にとっては「そんなの、ちゃんと部品共通化すればいいじゃん」という声もあろうかと思いますが、それでも部品が拡張されたときのことを考えれば、すべてを定数にするほうが、将来的に良いと私は確信しています。

定数にするメリット・デメリット

メリット

  • mapキーに共通の意味のつながりを持たせることができる(ServletRequest で多用されますね)
  • 定数からIDEで使用箇所をたどることができる(マイグレーションで大活躍)
  • 変更耐性が強い(拡張時に、定数だけ変更すればよいケースが多い)

デメリット

  • コード量が多くなる(単純にめんどくさい)

メリデメだけを見ても、やって損はないと思います。

まとめ:リテラル書いたら、定数にしよう

リテラルを書く機会って、本当にたくさんあります。
現代のプログラムは、ほとんどがフレームワークで作られます。
フレームワークでは、データにキー値で意味づけすることが多く、早く作れるので、都度都度リテラルを書いてしまいがちです。

ぜひリテラルをロジック中に書くことになったら、定数にすることをおススメします。
この説明だけで、そのメリットが十分に伝わっていなくても、近くきっとあなたにとって、 「やっててよかった」の瞬間があるはずです。

新規登録して、もっと便利にQiitaを使ってみよう

  1. あなたにマッチした記事をお届けします
  2. 便利な情報をあとで効率的に読み返せます
ログインすると使える機能について
TechnoKuRo
@TechnoKuRo(KuRo Techno)
元警察官のフリーランスエンジニアです。 まだSES案件で生計を立てて、ちょこちょこと受注しつつ暮らしています。 いずれはインフラもネットワークもソフトウェアもイケるエンジニアを目指します! 趣味はスノーボードとカポエィラ。 カポエィラは2つ目の帯をゲット!

コメント

(編集済み)

文字リテラルやString型の識別子をオブジェクトの識別子として用いると、他者が「キー名は異なるが値が同じである」識別子を使って無理やりアクセスしたり、決まりごとに反し結局文字リテラルでアクセスすることがあるかもしれません。
オブジェクトの識別子を定義するなら、enumとEnumMapの併用が使えます。

enum Key {
    /** スリーサイズのバスト */
    THREE_SIZE_BUST,
    /** スリーサイズのウエスト */
    THREE_SIZE_WEST,
    /** スリーサイズのヒップ */
    THREE_SIZE_HIP,
}

/** データベースから name のスリーサイズをmapで返却 */
public static Map<Key, Integer> getThreeSize(String name){
    ThreeSizeDto threeSizeDto = threeSizeDb.getThreeSize(name);
    Map<Key, Integer> retValue = new EnumMap<>(Key.class);
    retValue.put(Key.THREE_SIZE_BUST , threeSizeDto.getBust());
    retValue.put(Key.THREE_SIZE_WEST , threeSizeDto.getWest());
    retValue.put(Key.THREE_SIZE_HIP , threeSizeDto.getHip());
    return retValue;
}

/** スリーサイズのmapを生成 */
public static Map<Key, Integer> setThreeSize(Integer bust, Integer west, Integer hip){
    Map<Key, Integer> retValue = new EnumMap<>(Key.class);
    retValue.put(Key.THREE_SIZE_BUST , bust);
    retValue.put(Key.THREE_SIZE_WEST , threeSizeDto.getWest());
    retValue.put(Key.THREE_SIZE_HIP , threeSizeDto.getHip());
    return retValue;
}

public static void main(String[] args){
    Map<Key, Integer> threeSizeMap = getThreeSize(args[0]);
    System.out.println(args[0] + "さんのバストサイズは" + threeSizeMap.get(Key.THREE_SIZE_BUST));
}

enumで定義される要素(列挙子)はそれ以外のインスタンスを生成できないため、こうすれば他者が列挙子以外の値を使ってアクセスすることはできなくなります。

1
(編集済み)

@muchosucho
コメントありがとうございます。

確かにいただいた enum のご提案は素晴らしいものですね。
とても理想的です。

強いて申し上げると、今回の読者ターゲット(記載しておけばよかったと反省)が初学者向けと、とある企業さんフレームワークを利用してのコーディングを見ていて気になったときにこの記事を書いた流れから、String定数でのご紹介にした経緯がありました。

おそらく多くのWEB系フレームワークですと、httpプロトコルの受け口として String型をキーとして利用していることが多いと(個人的に)感じているし、そこでリテラルを使いまくっているソースコードを大量生産している方々が多いと(私の経験上)感じているため、こういう記事にしました。

この点をご理解の上、さらなる提案がございましたらぜひともお聞かせください。

ありがとうございます。

1
あなたもコメントしてみませんか :)
新規登録
すでにアカウントを持っている方はログイン
記事投稿キャンペーン開催中
記事投稿キャンペーン 「エンジニア×非エンジニアのコミュニケーション」
~
記事投稿キャンペーン 「プロダクトマネジメント」
~
1

Qiitaにログインしてダークテーマを使ってみませんか?🌙

ログインするとOSの設定にあわせたテーマカラーを使用できます!