{{ ... }}
Javaのダブルブレース初期化構文( )とは?
13 に答える
二重ブレースの初期化は、指定されたクラス (外側のブレース)から派生した無名クラスを作成し、そのクラス (内側のブレース)内に初期化ブロックを提供します。例えば
new ArrayList<Integer>() {{
add(1);
add(2);
}};
この二重ブレースの初期化を使用すると、匿名の内部クラスが作成されることに注意してください。this
作成されたクラスには、周囲の外部クラスへの暗黙的なポインターがあります。通常は問題にはなりませんが、シリアル化やガベージ コレクションなどの状況では問題が発生する可能性があるため、注意が必要です。
誰かが二重ブレースの初期化を使用するたびに、子猫が殺されます。
構文がかなり変わっていて、実際には慣用的ではない (もちろん、味については議論の余地があります) ことを除けば、アプリケーションに 2 つの重大な問題を不必要に作成していることになります。
1. あまりにも多くの匿名クラスを作成している
二重ブレースの初期化を使用するたびに、新しいクラスが作成されます。たとえば、この例:
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
...これらのクラスを生成します:
Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class
これは、クラスローダーにとってかなりのオーバーヘッドです。もちろん、一度やれば初期化時間はそれほどかかりません。しかし、エンタープライズ アプリケーション全体でこれを 20,000 回行うとしたら、ほんの少しの「シンタックス シュガー」のためだけにヒープ メモリが必要になるのでしょうか?
2. メモリ リークが発生している可能性があります。
上記のコードを使用してメソッドからそのマップを返す場合、そのメソッドの呼び出し元は無意識のうちにガベージ コレクションできない非常に重いリソースを保持している可能性があります。次の例を検討してください。
public class ReallyHeavyObject {
// Just to illustrate...
private int[] tonsOfValues;
private Resource[] tonsOfResources;
// This method almost does nothing
public Map quickHarmlessMethod() {
Map source = new HashMap(){{
put("firstName", "John");
put("lastName", "Smith");
put("organizations", new HashMap(){{
put("0", new HashMap(){{
put("id", "1234");
}});
put("abc", new HashMap(){{
put("id", "5678");
}});
}});
}};
return source;
}
}
返さMap
れた には、 の外側のインスタンスへの参照が含まれるようになりましたReallyHeavyObject
。あなたはおそらくそれを危険にさらしたくないでしょう:
http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/からの画像
3. Java がマップ リテラルを持っているふりをすることができます
あなたの実際の質問に答えるために、人々はこの構文を使用して、Java が既存の配列リテラルに似たマップ リテラルのようなものを持っているふりをしています。
String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};
一部の人々は、これが構文的に刺激的であると感じるかもしれません。
- 最初の中かっこは、新しい匿名内部クラスを作成します。
- 中括弧の 2 番目のセットは、クラスの静的ブロックのようなインスタンス初期化子を作成します。
例えば:
public class TestHashMap {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<String,String>(){
{
put("1", "ONE");
}{
put("2", "TWO");
}{
put("3", "THREE");
}
};
Set<String> keySet = map.keySet();
for (String string : keySet) {
System.out.println(string+" ->"+map.get(string));
}
}
}
使い方
最初の中かっこは、新しい匿名内部クラスを作成します。これらの内部クラスは、親クラスの動作にアクセスできます。したがって、私たちの場合、実際には HashSet クラスのサブクラスを作成しているので、この内部クラスは put() メソッドを使用できます。
中括弧の 2 番目のセットは、インスタンスの初期化子にすぎません。コア Java の概念を覚えていれば、構造体のような同様のブレースにより、インスタンス初期化子ブロックを静的初期化子に簡単に関連付けることができます。唯一の違いは、静的初期化子が static キーワードで追加され、一度だけ実行されることです。オブジェクトの数に関係なく。
二重ブレースの初期化の楽しいアプリケーションについては、ここDwemthy's Array in Javaを参照してください。
抜粋
private static class IndustrialRaverMonkey
extends Creature.Base {{
life = 46;
strength = 35;
charisma = 91;
weapon = 2;
}}
private static class DwarvenAngel
extends Creature.Base {{
life = 540;
strength = 6;
charisma = 144;
weapon = 50;
}}
BattleOfGrottoOfSausageSmells
そして今、そして…分厚いベーコンの準備をしてください!
1- 二重ブレースなどはありません:
二重ブレースの初期化などは存在しないことを指摘したいと思います。通常の従来の 1 つのブレース初期化ブロックのみがあります。2 番目の中かっこブロックは、初期化とは関係ありません。答えは、これらの 2 つのブレースが何かを初期化すると言っていますが、そうではありません。
2-匿名クラスだけでなく、すべてのクラスについてです。
ほとんどすべての回答は、匿名の内部クラスを作成するときに使用されるものであると述べています。これらの回答を読んだ人は、匿名の内部クラスを作成するときにのみ使用されるという印象を受けると思います。しかし、それはすべてのクラスで使用されます。これらの回答を読むと、匿名クラス専用のまったく新しい特別な機能のように見えますが、それは誤解を招くと思います。
3- 目的は括弧を互いに配置することであり、新しい概念ではありません:
さらに進んで、この質問は、2 番目の開始ブラケットが最初の開始ブラケットの直後にある場合の状況について話します。通常のクラスで使用する場合、通常、2 つのブレースの間に何らかのコードがありますが、まったく同じものです。したがって、ブラケットを配置することは問題です。ですから、これは何か新しいエキサイティングなことだと言うべきではないと思います。なぜなら、これは私たち全員が知っていることですが、括弧内にいくつかのコードで書かれているだけだからです。「二重ブレースの初期化」という新しい概念を作成するべきではありません。
4- ネストされた無名クラスの作成は、2 つの中括弧とは関係
ありません。無名クラスを作成しすぎるという議論には同意しません。初期化ブロックのためにそれらを作成しているのではなく、単にそれらを作成したからです。これらの問題は、2 つのブレースの初期化を使用しなくても作成されるため、初期化を行わなくても発生します... 初期化は、初期化されたオブジェクトを作成する要因ではありません。
さらに、この存在しないもの「二重括弧の初期化」を使用して作成された問題、または通常の 1 つの括弧の初期化によって作成された問題については話すべきではありません。説明されている問題は、匿名クラスを作成したためにのみ存在するため、元の質問とは関係ありません。しかし、すべての回答は、匿名クラスを作成したことのせいではなく、「二重ブレースの初期化」と呼ばれるこの邪悪な (存在しない) ものであるという印象を読者に与えます。
次のような、二重ブレースの初期化によるすべての悪影響を回避するには:
- 壊れた「等しい」互換性。
- 直接割り当てを使用する場合、チェックは実行されません。
- メモリ リークの可能性があります。
次のことを行います:
- 特に二重ブレースの初期化のために、別の「ビルダー」クラスを作成します。
- デフォルト値でフィールドを宣言します。
- そのクラスにオブジェクト作成メソッドを入れます。
例:
public class MyClass {
public static class Builder {
public int first = -1 ;
public double second = Double.NaN;
public String third = null ;
public MyClass create() {
return new MyClass(first, second, third);
}
}
protected final int first ;
protected final double second;
protected final String third ;
protected MyClass(
int first ,
double second,
String third
) {
this.first = first ;
this.second= second;
this.third = third ;
}
public int first () { return first ; }
public double second() { return second; }
public String third () { return third ; }
}
使用法:
MyClass my = new MyClass.Builder(){{ first = 1; third = "3"; }}.create();
利点:
- 使い方は簡単。
- 「等しい」互換性を壊さないでください。
- 作成方法でチェックを行うことができます。
- メモリリークはありません。
短所:
- なし。
その結果、これまでで最も単純な Java ビルダー パターンができあがりました。
github ですべてのサンプルを参照してください: java-sf-builder-simple-example
コレクションを初期化するために、いくつかの Java ステートメントをループとして配置できます。
List<Character> characters = new ArrayList<Character>() {
{
for (char c = 'A'; c <= 'E'; c++) add(c);
}
};
Random rnd = new Random();
List<Integer> integers = new ArrayList<Integer>() {
{
while (size() < 10) add(rnd.nextInt(1_000_000));
}
};
ただし、この場合はパフォーマンスに影響します。このディスカッションを確認してください
これは、コレクションを初期化するためのショートカットです。もっと詳しく知る ...
このようなことを意味しますか?
List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};
作成時の配列リストの初期化です(ハック)
これは、flash や vbscript でよく使用される with キーワードと同じように見えます。それは、現状を変える方法であり、それ以上のものthis
ではありません。