5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Double Brace Initialization (DBI)って?

Last updated at Posted at 2019-10-24

はじめに

つい先日、SonarQubeを使って他の人のコードを解析してみたとき、次のようなコードに、マイナーだがバグの可能性があることをSonar先生が指摘されていました。

Set<String> myLanguageSet = new HashSet<String>() {
    {
        add("Java");
        add("JavaScript");
        add("PHP");
    }
};

ぼく「あれれ〜、おっかしいぞー!括弧が2つもあるよ」
Sonar先生「

Double Brace Initialization should not be used
Because Double Brace Initialization (DBI) creates an anonymous class with a reference to the instance of the owning object, its use can lead to memory leaks if the anonymous inner class is returned and held by other objects. Even when there's no leak, DBI is so obscure that it's bound to confuse most maintainers.
For collections, use Arrays.asList instead, or explicitly add each item directly to the collection.


ぼく「なるほど、完全に理解した。」

Double Brace Initializationって?

日本語に訳すと「二重括弧初期化」?そのままカタカナで「ダブルブレースイニシャライゼーション」?知っている方いたら教えてくださいmm
今回はSonar先生と同じく「DBI」と呼ばせてください。

DBIは2つのプロセスの組み合わせです。
まず最初の括弧が匿名クラスを生成します。
そして次の括弧が初期化ブロックでオブジェクトを初期化しています。
ひとずつ確認していきましょう。

匿名クラス

Javaの教材だと、よくインターフェイスのところで出てくるやつですね。例えば一度しか使わないのにインターフェイスを実体化するクラスを作り、コードが管理しにくくなったり可読性が下がったりしてしまいます。その煩わしさを匿名クラスは解決してくれます。
詳しくは他の方が説明されているので、ここでは簡単(で雑)な例を掲載します。

public interface Greeting {
    public void sayHello();
}
// 匿名クラスを利用しない場合
public class GreetingImple implements Greeting {
    @Override 
    public void sayHallo() {
        System.out.println("Hello");
    }
}

public class Sample1 {
    public static void main(String[] args) {
        Greeting greeting = new GreetingImple();
        greeting.sayHello();
    }
}
// 匿名クラスを利用した場合
public class Sample2 {
    public static void main(String[] args) {
        Greeting greeting = new Greeting() {
            @Override 
            public void sayHallo() {
                System.out.println("Hello");
            }
        };
        greeting.sayHello();
    }
}

初期化ブロック

初期化ブロックにはオブジェクト初期化ブロック静的初期化ブロックがあります。今回は前者のみとりあげます。
変数の初期化はイメージしやすいと思います。それをただオブジェクトでもやるだけです。書き方は少し変数の初期化とは異なり、{ }で処理を囲います。
オブジェクトの初期化はオブジェクト毎に実行され、実行タイミングはコンストラクタより先です。

Public class InitializeSample {
    // 初期化ブロック
    {
        System.out.println("初期化ブロック1");
    }
    // コンストラクタ
    public InitializeSample() {
        System.out.println("コンストラクタ");
    }
    // 初期化ブロック
    {
        System.out.println("初期化ブロック2");
    }

実行結果
初期化ブロック1
初期化ブロック2
コンストラクタ

ようやくDBIのお話。

上の2つを組み合わせます。匿名クラスに初期化ブロックを適用するとDBIになります。通常このように

Set<String> myLanguageSet = new HashSet<>();
myLanguageSet.add("Java");
myLanguageSet.add("JavaScript");
myLanguageSet.add("PHP");

というように実装してあげるのですが、DBIだと

Set<String> myLanguageSet = new HashSet<String>() {
    {
        add("Java");
        add("JavaScript");
        add("PHP");
    }
};

というように、HashSetを匿名で作成し、add("Java"); add("JavaScript"); add("PHP");で初期化してあげる形で実装することができます。

メリット

ではわざわざこのように実装するメリットはなんでしょう。

コードの記述量が少なくなる

たしかに、上記の例だと毎回myLanguageSet.addは面倒ですよね(コピペしろ)

可読性が上がる

慣れると見やすい気がします。SetとかListのスコープがわかりやすいです。

デメリット

メリットがあればもちろんデメリットもあります。

そこまで広く知られた書き方じゃない

初見だと何が起きてるかよくわからないです。

ダイアモンド演算子が使えない

上記の例のようにnew HashSet<String>()と書かなきゃいけません。匿名クラスでは<>は使用できません。

メモリリークを起こす可能性がある

匿名クラスはそれを内包するインスタンスに対して、見えないthisポインターをもつそうで、これがメモリリークを起こすだけでなく、Serialize系でも支障をきたすようです。

結局DBIってどうなの?

上記のように、メリットよりデメリットの方があるので、DBIはけっこうアンチパターンと考えられているそうです。特別、理由がない限りはDBIは避けましょう。
代替案として、Java9以降であればList.ofSet.ofを使ったり、Java8であればStream APIを使うのが良いみたいです。

// java9
List<String> list = List.of("aaa", "bbb", "ccc");
Set<String> set = Set.of("aaa", "bbb", "ccc");

// java 8
Set<String> countries = Stream.of("aaa", "bbb", "ccc")
      .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

参考にしたサイト

https://www.baeldung.com/java-double-brace-initialization
https://javapapers.com/core-java/java-double-brace-initialization/
https://stackoverflow.com/questions/1958636/what-is-double-brace-initialization-in-java

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?