web-dev-qa-db-ja.com

Javaの "二重ブレース初期化"の効率性

Javaの隠された機能 では、トップアンサーが 二重括弧の初期化 を挙げて、 とても魅力的な構文

Set<String> flavors = new HashSet<String>() {{
    add("Vanilla");
    add("strawberry");
    add("chocolate");
    add("butter pecan");
}};

この慣用句は、インスタンス初期化子だけを含む匿名内部クラスを作成します。これは、「包含スコープ内で任意の[...]メソッドを使用できます」。

主な質問:は非効率的です。その使用は一回限りの初期化に限定されるべきですか? (そしてもちろん披露!)

2番目の質問:新しいHashSetはインスタンス初期化子で使われている "this"でなければなりません…誰もがメカニズムを解明できますか?

3番目の質問:この慣用句も本番用コードでは使用できません

要約:とても、とてもいい答え、みんなありがとう。質問(3)に関して、人々は構文が明確であるべきだと感じました(特にあなたのコードがそれに精通していないかもしれない開発者に渡るなら私は時折のコメントをお勧めします)。

質問(1)では、生成されたコードは素早く実行されるはずです。余分な.classファイルはjarファイルを乱雑にし、プログラムの起動をわずかに遅くします(それを測定してくれた@coobirdに感謝します)。 @Thiloさんは、ガベージコレクションが影響を受ける可能性があることを指摘しました。追加のロードクラスのメモリコストが、場合によっては要因になる可能性があります。

質問(2)は私にとって最も興味深いことがわかりました。答えを理解すると、DBIで起こっていることは、匿名内部クラスがnew演算子によって構成されているオブジェクトのクラスを拡張し、したがって構成されているインスタンスを参照する "this"値を持つということです。とてもきれいです。

全体的に見て、DBIは私を知的な好奇心のようなものとして捉えています。 Coobirdらは、Arrays.asList、可変引数メソッド、Google Collections、および提案されたJava 7 Collectionリテラルを使用しても同じ効果が得られると指摘しています。 Scala、JRuby、Groovyなどの新しいJVM言語でも、リスト作成のための簡潔な表記法が提供されており、Javaとうまく相互運用できます。 DBIがクラスパスを乱雑にし、クラスのロードを少し遅くし、コードをもう少しわかりにくくすることを考えると、私はおそらくそれを避けます。しかし、私はSCJPを手に入れたばかりで、Javaのセマンティクスについて気の利いた話題が大好きな友人に、これを伝えたいと思います。 ;-) みんな、ありがとう!

7/2017:Baeldung は二重括弧の初期化に関する良い要約 を持ち、それをアンチパターンと見なします。

12/2017:@Basil Bourqueは、新しいJava 9ではあなたが言うことができると述べている:

Set<String> flavors = Set.of("Vanilla", "strawberry", "chocolate", "butter pecan");

確かにそれは進むべき道です。以前のバージョンを使用している場合は、 Google CollectionsのImmutableSet をご覧ください。

759
Jim Ferrans

匿名の内部クラスに夢中になったときの問題は、次のとおりです。

2009/05/27  16:35             1,602 DemoApp2$1.class
2009/05/27  16:35             1,976 DemoApp2$10.class
2009/05/27  16:35             1,919 DemoApp2$11.class
2009/05/27  16:35             2,404 DemoApp2$12.class
2009/05/27  16:35             1,197 DemoApp2$13.class

/* snip */

2009/05/27  16:35             1,953 DemoApp2$30.class
2009/05/27  16:35             1,910 DemoApp2$31.class
2009/05/27  16:35             2,007 DemoApp2$32.class
2009/05/27  16:35               926 DemoApp2$33$1$1.class
2009/05/27  16:35             4,104 DemoApp2$33$1.class
2009/05/27  16:35             2,849 DemoApp2$33.class
2009/05/27  16:35               926 DemoApp2$34$1$1.class
2009/05/27  16:35             4,234 DemoApp2$34$1.class
2009/05/27  16:35             2,849 DemoApp2$34.class

/* snip */

2009/05/27  16:35               614 DemoApp2$40.class
2009/05/27  16:35             2,344 DemoApp2$5.class
2009/05/27  16:35             1,551 DemoApp2$6.class
2009/05/27  16:35             1,604 DemoApp2$7.class
2009/05/27  16:35             1,809 DemoApp2$8.class
2009/05/27  16:35             2,022 DemoApp2$9.class

これらはすべて、単純なアプリケーションを作成したときに生成されたクラスで、大量の匿名内部クラスを使用しています。各クラスは別々のclassファイルにコンパイルされます。

「二重括弧の初期化」は、すでに述べたように、インスタンス初期化ブロックを持つ匿名の内部クラスです。つまり、「初期化」ごとに新しいクラスが作成されます。これは、通常単一のオブジェクトを作成するためです。

Java仮想マシンはそれらを使用するときにそれらすべてのクラスを読み取る必要があることを考えると、 バイトコード検証 プロセスなどで時間がかかる可能性があります。これらすべてのclassファイルを格納するために必要なディスク容量の増加は言うまでもありません。

二重括弧の初期化を利用するときに少しオーバーヘッドがあるように思われるので、それを使い過ぎるのはおそらくあまり良い考えではありません。しかし、Eddieがコメントで述べているように、その影響を絶対に確信することは不可能です。


参考までに、二重括弧の初期化は次のとおりです。

List<String> list = new ArrayList<String>() {{
    add("Hello");
    add("World!");
}};

それはJavaの「隠された」機能のように見えますが、それは単なる書き換えです。

List<String> list = new ArrayList<String>() {

    // Instance initialization block
    {
        add("Hello");
        add("World!");
    }
};

それで基本的に インスタンス初期化ブロック - は無名の内部クラスの一部です


Joshua Blochの コレクションリテラルの提案 / Project Coin は次のような流れに沿っていました。

List<Integer> intList = [1, 2, 3, 4];

Set<String> strSet = {"Apple", "Banana", "Cactus"};

Map<String, Integer> truthMap = { "answer" : 42 };

悲しいことに、それは そのやり方をしなかった Java 7と8のどちらにもならず、無期限に棚上げされた。


実験

これが私がテストした簡単な実験です。2つの方法を使用して、ArrayListメソッドを介して要素"Hello""World!"を追加した1000個のaddsを作成します。

方法1:二重ブレースの初期化

List<String> l = new ArrayList<String>() {{
  add("Hello");
  add("World!");
}};

方法2:ArrayListおよびaddをインスタンス化する

List<String> l = new ArrayList<String>();
l.add("Hello");
l.add("World!");

2つの方法で1000の初期化を実行するためのJavaソースファイルを書き出す簡単なプログラムを作成しました。

テスト1:

class Test1 {
  public static void main(String[] s) {
    long st = System.currentTimeMillis();

    List<String> l0 = new ArrayList<String>() {{
      add("Hello");
      add("World!");
    }};

    List<String> l1 = new ArrayList<String>() {{
      add("Hello");
      add("World!");
    }};

    /* snip */

    List<String> l999 = new ArrayList<String>() {{
      add("Hello");
      add("World!");
    }};

    System.out.println(System.currentTimeMillis() - st);
  }
}

テスト2:

class Test2 {
  public static void main(String[] s) {
    long st = System.currentTimeMillis();

    List<String> l0 = new ArrayList<String>();
    l0.add("Hello");
    l0.add("World!");

    List<String> l1 = new ArrayList<String>();
    l1.add("Hello");
    l1.add("World!");

    /* snip */

    List<String> l999 = new ArrayList<String>();
    l999.add("Hello");
    l999.add("World!");

    System.out.println(System.currentTimeMillis() - st);
  }
}

1000個のArrayListsとArrayListを拡張する1000個の匿名内部クラスを初期化するための経過時間はSystem.currentTimeMillisを使用してチェックされるので、タイマーの分解能はそれほど高くありません。私のWindowsシステムでは、解像度はおよそ15-16ミリ秒です。

2回のテストを10回実行した結果は次のとおりです。

Test1 Times (ms)           Test2 Times (ms)
----------------           ----------------
           187                          0
           203                          0
           203                          0
           188                          0
           188                          0
           187                          0
           203                          0
           188                          0
           188                          0
           203                          0

見て分かるように、二重括弧の初期化は約190msの顕著な実行時間を有する。

一方、ArrayList初期化実行時間は0msとなりました。もちろん、タイマーの解決は考慮に入れられるべきですが、それは15ミリ秒未満である可能性があります。

そのため、2つのメソッドの実行時間には大きな違いがあるようです。 2つの初期化方法には確かにいくらかのオーバーヘッドがあるようです。

そして、.classダブルブレース初期化テストプログラムをコンパイルして生成されたTest1ファイルは1000個ありました。

572
coobird

これまで指摘されていなかったこのアプローチの1つの特性は、内部クラスを作成するため、包含クラス全体がそのスコープ内に取り込まれることです。これは、あなたのSetが生きている限り、それは含んでいるインスタンスへのポインタ(this$0)を保持し、それがガベージコレクションされないようにすることを意味します。

これ、そして通常のHashSetがうまく機能するにもかかわらず、新しいクラスが最初から作成されるという事実から、この構文を使用したくありません(たとえ私が構文糖には長い時間がかかっても)。

2番目の質問:新しいHashSetはインスタンス初期化子で使われている "this"でなければなりません…誰もがメカニズムを解明できますか?私は素朴に「これ」が「フレーバー」を初期化するオブジェクトを指すことを期待していたはずです。

これは内部クラスが機能する方法です。これらは独自のthisを取得しますが、親インスタンスへのポインタも持っているので、包含オブジェクトのメソッドも呼び出すことができます。名前の競合がある場合は、内部クラス(あなたの場合はHashSet)が優先されますが、外部メソッドも取得するために "this"の前にクラス名を付けることができます。

public class Test {

    public void add(Object o) {
    }

    public Set<String> makeSet() {
        return new HashSet<String>() {
            {
              add("hello"); // HashSet
              Test.this.add("hello"); // outer instance 
            }
        };
    }
}

作成されている匿名サブクラスを明確にするために、そこにメソッドを定義することもできます。例えばHashSet.add()をオーバーライドする

    public Set<String> makeSet() {
        return new HashSet<String>() {
            {
              add("hello"); // not HashSet anymore ...
            }

            @Override
            boolean add(String s){

            }

        };
    }
96
Thilo

誰かが二重ブレースの初期化を使用するたびに、子猫は殺されます。

構文がかなり普通ではなく、あまり一般的ではない(味はもちろん議論の余地がある)ことを除けば、アプリケーションで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を囲むインスタンスへの参照が含まれるようになります。あなたはおそらくそれを危険にさらしたくないでしょう:

Memory Leak Right Here

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"); }};

一部の人々は、これが構文的に刺激的だと感じるかもしれません。

47
Lukas Eder

漏れやすい

パフォーマンスへの影響には、ディスク操作+ unzip(jar用)、クラス検証、perm-genスペース(SunのHotspot JVM用)が含まれます。ただし、最悪の場合、リークが発生しやすくなります。あなたは単に戻ることはできません。

Set<String> getFlavors(){
  return Collections.unmodifiableSet(flavors)
}

そのため、セットが別のクラスローダによってロードされた他の部分にエスケープされ、そこに参照が保持されていると、クラス+クラスローダのツリー全体がリークされます。それを避けるために、HashMapへのコピーが必要です、new LinkedHashSet(new ArrayList(){{add("xxx);add("yyy");}})。もうそれほど可愛くない。私はイディオムを使いません、代わりにnew LinkedHashSet(Arrays.asList("xxx","YYY"));のようなものです

35
bestsss

次のテストクラスを受講する:

public class Test {
  public void test() {
    Set<String> flavors = new HashSet<String>() {{
        add("Vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
    }};
  }
}

そしてクラスファイルを逆コンパイルすると、次のようになります。

public class Test {
  public void test() {
    Java.util.Set flavors = new HashSet() {

      final Test this$0;

      {
        this$0 = Test.this;
        super();
        add("Vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
      }
    };
  }
}

これは私にとってひどく非効率的には見えません。私がこのような何かのための性能について心配していたならば、私はそれをプロファイルしたいと思います。そしてあなたの質問#2は上記のコードで答えられます:あなたはあなたの内部クラスのための暗黙的なコンストラクタ(およびインスタンス初期化子)の中にいるので、 "this"はこの内部クラスを表します。

はい、この構文はあいまいですが、コメントによってわかりにくい構文の使用方法が明確になります。構文を明確にするために、ほとんどの人は静的初期化子ブロック(JLS 8.7静的初期化子)に精通しています。

public class Sample1 {
    private static final String someVar;
    static {
        String temp = null;
        ..... // block of code setting temp
        someVar = temp;
    }
}

私はこれがプロダクションコードで使われるのを見たことがないけれども、あなたはコンストラクタの使用法(JLS 8.6インスタンス初期化子)のために同様の構文(Word "static"なしで)を使うこともできます。これはあまり一般的ではありません。

public class Sample2 {
    private final String someVar;

    // This is an instance initializer
    {
        String temp = null;
        ..... // block of code setting temp
        someVar = temp;
    }
}

デフォルトのコンストラクタがない場合、{}の間のコードブロックはコンパイラによってコンストラクタに変換されます。これを念頭に置いて、二重括弧のコードを解きます。

public void test() {
  Set<String> flavors = new HashSet<String>() {
      {
        add("Vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
      }
  };
}

最も内側の中括弧の間のコードブロックは、コンパイラによってコンストラクタに変換されます。最も外側の中括弧は、匿名の内部クラスを区切ります。これをすべて非匿名にする最後のステップにするには、次のようにします。

public void test() {
  Set<String> flavors = new MyHashSet();
}

class MyHashSet extends HashSet<String>() {
    public MyHashSet() {
        add("Vanilla");
        add("strawberry");
        add("chocolate");
        add("butter pecan");
    }
}

初期化の目的では、オーバーヘッドはまったくありません(または無視できるほど小さいため)。ただし、flavorsを使用するたびにHashSetに対してではなく、代わりにMyHashSetに対して使用されます。これにはおそらくわずかな(そしておそらく無視できる程度の)オーバーヘッドがあります。しかし、私がそれについて心配する前に、私はそれをプロファイルします。

繰り返しますが、あなたの質問2では、上記のコードは論理的かつ明示的に二重括弧の初期化と同等であり、 "this"が指す場所を明確にしています。HashSetを拡張する内部クラス。

インスタンス初期化子の詳細について質問がある場合は、 JLS のドキュメントで詳細を調べてください。

35
Eddie

多数のクラスをロードすると、開始に数ミリ秒かかることがあります。起動がそれほど重要ではなく、起動後のクラスの効率を調べても違いはありません。

package Vanilla.Java.perfeg.doublebracket;

import Java.util.*;

/**
 * @author plawrey
 */
public class DoubleBracketMain {
    public static void main(String... args) {
        final List<String> list1 = new ArrayList<String>() {
            {
                add("Hello");
                add("World");
                add("!!!");
            }
        };
        List<String> list2 = new ArrayList<String>(list1);
        Set<String> set1 = new LinkedHashSet<String>() {
            {
                addAll(list1);
            }
        };
        Set<String> set2 = new LinkedHashSet<String>();
        set2.addAll(list1);
        Map<Integer, String> map1 = new LinkedHashMap<Integer, String>() {
            {
                put(1, "one");
                put(2, "two");
                put(3, "three");
            }
        };
        Map<Integer, String> map2 = new LinkedHashMap<Integer, String>();
        map2.putAll(map1);

        for (int i = 0; i < 10; i++) {
            long dbTimes = timeComparison(list1, list1)
                    + timeComparison(set1, set1)
                    + timeComparison(map1.keySet(), map1.keySet())
                    + timeComparison(map1.values(), map1.values());
            long times = timeComparison(list2, list2)
                    + timeComparison(set2, set2)
                    + timeComparison(map2.keySet(), map2.keySet())
                    + timeComparison(map2.values(), map2.values());
            if (i > 0)
                System.out.printf("double braced collections took %,d ns and plain collections took %,d ns%n", dbTimes, times);
        }
    }

    public static long timeComparison(Collection a, Collection b) {
        long start = System.nanoTime();
        int runs = 10000000;
        for (int i = 0; i < runs; i++)
            compareCollections(a, b);
        long rate = (System.nanoTime() - start) / runs;
        return rate;
    }

    public static void compareCollections(Collection a, Collection b) {
        if (!a.equals(b) && a.hashCode() != b.hashCode() && !a.toString().equals(b.toString()))
            throw new AssertionError();
    }
}

版画

double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 34 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
double braced collections took 36 ns and plain collections took 36 ns
19
Peter Lawrey

セットを作成するには、二重ブレースの初期化の代わりに可変引数ファクトリメソッドを使用できます。

public static Set<T> setOf(T ... elements) {
    return new HashSet<T>(Arrays.asList(elements));
}

Google Collectionsライブラリには、このような便利な方法がたくさんあり、その他の便利な機能もたくさんあります。

慣用句のあいまいさについては、私はそれに遭遇し、それをプロダクションコードで常に使用しています。私は、慣用句が本番用コードを書くことを許されていることに混乱するプログラマーについてもっと心配するでしょう。

16
Nat

効率性はさておき、私は自分自身で単体テスト以外の宣言的コレクションの作成を望んでいるとはほとんど思いません。二重括弧の構文は非常に読みやすいと思います。

リストを宣言的に構築するためのもう1つの方法は、Arrays.asList(T ...)を次のように使用することです。

List<String> aList = Arrays.asList("Vanilla", "strawberry", "chocolate");

この方法の限界はもちろん、生成される特定の種類のリストを制御できないことです。

9
Paul Morie

一般的にそれに関して特に非効率的なものは何もありません。 JVMにとって、サブクラスを作成してそれにコンストラクタを追加することは、一般的には重要ではありません。それは、オブジェクト指向言語では日常的なことです。これを行うことによって非効率を引​​き起こす可能性がある、かなり疑わしいケースが考えられます(たとえば、このサブクラスのために異なるクラスを混在させるという繰り返し呼び出されるメソッドがありますが、通常渡されるクラスは完全に予測可能です) - 後者の場合、JITコンパイラは最初の場合には不可能な最適化を行う可能性があります。しかし、本当に、私はそれが問題になるケースは非常に人為的だと思います。

私はあなたがたくさんの匿名クラスで "物事を整理する"のかどうかの観点から問題をもっと見るでしょう。大まかな手引きとして、イディオムを使用する以上に使用しないことを検討してください。たとえば、イベントハンドラ用の無名クラスです。

(2)では、あなたはオブジェクトのコンストラクタの中にいるので、 "this"はあなたが構築しているオブジェクトを指します。他のコンストラクタと違いはありません。

(3)に関しては、それは本当にあなたのコードを誰が管理しているかにかかっていると思います。あなたがこれを前もって知らないならば、私が使うことを提案するベンチマークは「あなたはJDKへのソースコードでこれを見ますか?」です。 (この場合、私は多くの匿名イニシャライザを見たことを思い出しません。そして、それが匿名クラスののみの内容である場合は絶対にありません)。中程度の規模のプロジェクトでは、ある時点または他の時点でプログラマにJDKのソースを理解することを本当に必要としていると私は主張するでしょう。そこで使用される構文または慣用句は「公正なゲーム」です。それ以外にも、誰がコードを保守しているかを管理できれば、その構文について他のユーザーにトレーニングを行わせ、そうでなければコメントまたは回避してください。

7
Neil Coffey

ダブルブレースの初期化は、メモリリークやその他の問題を引き起こす可能性がある不要なハックです。

この「トリック」を使用する正当な理由はありません。 Guavaは、静的ファクトリーとビルダーの両方を含むNice immutable collections を提供します。これにより、クリーンで読み取り可能なsafe構文。

質問の例は次のようになります。

Set<String> flavors = ImmutableSet.of(
    "Vanilla", "strawberry", "chocolate", "butter pecan");

これは短くて読みやすいだけでなく、 その他の回答 で説明されている二重括弧パターンに関する多くの問題を回避します。もちろん、直接構築されたHashMapと同様に機能しますが、危険でエラーが発生しやすく、より良いオプションがあります。

二重ブレースの初期化を検討している場合は、構文のトリックを利用するのではなく、APIを再検査するか、または 新しいAPIを導入する を使用して問題に適切に対処する必要があります。

エラーが発生しやすいこのアンチパターンにフラグを立てる

5
dimo414

私はこれを調査していて、有効な答えによって提供されたものよりももっと徹底的なテストをすることにしました。

コードは次のとおりです。 https://Gist.github.com/4368924

これが私の結論です

私は驚いたのですが、ほとんどの実行テストで内部開始が実際には早い(場合によっては2倍近く)ということです。大量の作業をしているときは、その効果は薄れていくようです。

興味深いことに、ループ上に3つのオブジェクトを作成すると、他のケースよりも早く利益が失われます。私はなぜこれが起こっているのか確信が持てませんし、結論に達するためにもっとテストをするべきです。具体的な実装を作成すると、クラス定義が再ロードされないようにすることができます(それが起こっている場合)。

しかし、多くの場合でも、1つのアイテムを作成するために多くのオーバーヘッドが見られないことは明らかです。

後退したことの1つは、二重中括弧の開始のそれぞれが、ディスクブロック全体をアプリケーションのサイズ(または圧縮時に約1k)に追加する新しいクラスファイルを作成することです。設置面積は小さいですが、さまざまな場所で使用されると影響を与える可能性があります。これを1000回使用すると、あなたは潜在的にあなたにMiB全体をあなたのアプリケーションに追加していることになります。

私の結論は?悪用されない限り、使用しても問題ありません。

どう考えているか教えてください :)

4
pablisco

私はNatの2番目の答えです。ただし、asList(elements)から暗黙のListを作成してすぐに投げるのではなく、ループを使用します。

static public Set<T> setOf(T ... elements) {
    Set set=new HashSet<T>(elements.size());
    for(T Elm: elements) { set.add(Elm); }
    return set;
    }
3
Lawrence Dol

この構文は便利かもしれませんが、ネストされているのでこの$ 0参照を多く追加するので、それぞれにブレークポイントが設定されていない限り、イニシャライザにデバッグをステップインすることは困難です。そのため、私はこれを平凡な設定者、特に定数や匿名のサブクラスが関係ない場所(直列化が関係しないような場所)にのみ設定することをお勧めします。

3
Eric Woodruff

Mario Gleichman はScalaリストのリテラルをシミュレートするためのJava 1.5総称関数の使い方を 説明しています、残念ながら immutable リスト。

彼はこのクラスを定義しています。

package literal;

public class collection {
    public static <T> List<T> List(T...elems){
        return Arrays.asList( elems );
    }
}

そしてそれを次のように使います。

import static literal.collection.List;
import static system.io.*;

public class CollectionDemo {
    public void demoList(){
        List<String> slist = List( "a", "b", "c" );
        List<Integer> iList = List( 1, 2, 3 );
        for( String elem : List( "a", "Java", "list" ) )
            System.out.println( elem );
    }
}

Googleコレクションは、 Guava の一部になりましたが、同様のリスト作成のアイデアをサポートしています。このインタビューで、Jared Levyはこう述べています。

[...]私が書くほとんどすべてのJavaクラスに見られる、最も頻繁に使われる機能は静的メソッドで、Javaコードの繰り返しのキーストロークの数を減らします。次のようなコマンドを入力できると便利です。

Map<OneClassWithALongName, AnotherClassWithALongName> = Maps.newHashMap();

List<String> animals = Lists.immutableList("cat", "dog", "horse");

7/10/2014:Pythonのように単純なものであれば

animals = ['cat', 'dog', 'horse']

3
Jim Ferrans
  1. これは各メンバに対してadd()を呼び出します。ハッシュセットに項目を入れるためのより効率的な方法を見つけることができるなら、それを使ってください。注意が必要な場合は、内部クラスがゴミを生成する可能性があります。

  2. コンテキストはnewから返されるオブジェクト、つまりHashSetであるかのようです。

  3. あなたが尋ねる必要があるならば…多分:あなたの後に来る人々はこれを知っているかどうか?理解し説明するのは簡単ですか?あなたが両方に「はい」と答えることができるならば、それを使ってください。

2
Jon Watte