web-dev-qa-db-ja.com

Java 8では、なぜ配列にIterableのforEachメソッドが与えられなかったのですか?

ここに何かが欠けているに違いない。

Java 5では、 "for-each loop"ステートメント(拡張forループとも呼ばれます が導入されました。主にCollectionsを反復処理するために導入されたようです。 Iterable インターフェイスを実装するコレクション(またはコンテナ)クラスは、「for-eachループ」を使用した反復に適格です。おそらく歴史的な理由で、Java配列はIterableインターフェースを実装していませんでした。しかし、配列は遍在するため、javacは配列でfor-eachループの使用を受け入れます(従来のforループと同等のバイトコードを生成します)。

Java 8では、 forEachメソッドIterableインターフェイスにdefaultメソッドとして追加されました。これにより、ラムダ式をコレクションに(反復中に)渡すことができました(例:list.forEach(System.out::println))。しかし、繰り返しますが、アレイはこの処理を享受しません。 (回避策があることを理解しています)。

拡張forループで配列を受け入れるように、forEachで配列を受け入れるようにjavacを拡張できなかった技術的な理由はありますか?配列にIterableを実装することなく、コード生成が可能になるようです。私は素朴ですか?

これは、構文が簡単であるため、配列をかなり自然に使用する言語の初心者にとって特に重要です。リストに切り替えてArrays.asList(1, 2, 3)を使用することはほとんど自然ではありません。

44
Kedar Mhaswade

Java言語および配列用のJVMには多くの特殊なケースがあります。配列にはAPIがありますが、ほとんど見えません。配列が次のように宣言されているようです。

  • implements Cloneable, Serializable
  • public final int length
  • public T[] clone()ここでTは配列のコンポーネントタイプです

ただし、これらの宣言はどこのソースコードにも表示されません。説明については、 JLS 4.10.3 および JLS 10.7 を参照してください。 CloneableおよびSerializableはリフレクションを介して表示され、呼び出しによって返されます

Object[].class.getInterfaces()

おそらく驚くべきことに、lengthフィールドとclone()メソッドは反射的に見えません。 lengthフィールドはまったくフィールドではありません。これを使用すると、特別なarraylengthバイトコードに変わります。 clone()を呼び出すと、実際の仮想メソッド呼び出しが行われますが、レシーバーが配列型の場合、これはJVMによって特別に処理されます。

ただし、配列クラスはIterableインターフェイスを実装していません。

拡張forループ( "for-each")がJava SE 5に追加されたとき、右辺式の2つの異なるケースをサポートしました:Iterableまたは配列型( JLS 14.14.2 )。その理由は、Iterableインスタンスと配列は、Enhanced-forステートメントによって完全に異なる方法で処理されるためです。 JLSのそのセクションは完全な処理を提供しますが、より簡単に言えば、状況は次のとおりです。

Iterable<T> iterableの場合、コード

for (T t : iterable) {
    <loop body>
}

の構文糖

for (Iterator<T> iterator = iterable.iterator(); iterator.hasNext(); ) {
    t = iterator.next();
    <loop body>
}

配列T[]の場合、コード

for (T t : array) {
    <loop body>
}

の構文糖

int len = array.length;
for (int i = 0; i < len; i++) {
    t = array[i];
    <loop body>
}

さて、なぜこのようにされたのですか?配列は既にIterableを実装できます。他のインターフェイスを既に実装しているためです。また、コンパイラーは、配列によってサポートされるIterator実装を合成することもできます。 (これには前例があります。コンパイラは、 JLS 8.9.3 で説明されているように、すべてのenumクラスに自動的に追加される静的values()およびvalueOf()メソッドを既に合成しています。)

ただし、配列は非常に低レベルの構造であり、int値を使用して配列にアクセスすると、非常に安価な操作が期待されます。 0から配列の長さまでループインデックスを実行し、毎回1ずつ増分するのは非常に慣用的です。配列の拡張forループはまさにそれを行います。配列の拡張forループがIterableプロトコルを使用して実装されている場合、配列のループに最初のメソッド呼び出しとメモリ割り当て(Iteratorの作成)が含まれ、その後に2つのメソッド呼び出しが含まれることを発見すると、ほとんどの人が不快に思うと思いますループ反復ごと。

したがって、Java 8のIterableにデフォルトのメソッドが追加された場合、これは配列にまったく影響しませんでした。

他の人が指摘したように、intlongdouble、または参照型の配列がある場合、Arrays.stream()呼び出しのいずれかを使用してこれをストリームに変えることができます。これにより、map()filter()forEach()などにアクセスできます。

ただし、配列のJava言語およびJVMの特殊なケースがreal構造に置き換えられた場合は素晴らしいでしょう2次元配列の不適切な処理、2 ^ 31の長さ制限など、その他の配列関連の問題を修正します。これは、ジョンローズが率いる「Arrays 2.0」調査の主題です。 JVMLS 2012でのジョンの講演を参照してください( videoslides )。この議論に関連するアイデアには、配列への実際のインターフェースの導入が含まれます。これにより、ライブラリが要素へのアクセスを中断したり、スライスやコピーなどの追加操作をサポートしたりできます。

これらはすべて調査と今後の作業であることに注意してください。この記事の執筆時点(2016-02-23)で、これらの配列の機能強化からリリースのJavaロードマップにコミットされているものはありません。

43
Stuart Marks

特別なコードがJavaコンパイラに追加されてforEachを処理するとします。その後、多くの同様の質問をすることができます。なぜmyArray.fill(0)を記述できないのですか?またはmyArray.copyOfRange(from, to)?またはmyArray.sort()myArray.binarySearch()myArray.stream()Arraysインターフェースのすべての静的メソッドは、実質的に「配列クラス」の対応するメソッドに変換できます。 JDK開発者がmyArray.forEach()で停止する必要があるのはなぜですか?ただし、このようなメソッドはすべて、classlib仕様だけでなく、はるかに安定した保守的なJava言語仕様に追加する必要があることに注意してください。また、これは、そのようなメソッドの実装が仕様の一部になるだけでなく、Java.util.function.ConsumerなどのクラスもJLSで明示的に言及する必要があることを意味します(提案されたforEachメソッドの引数)。また、対応する配列タイプのFloatConsumerByteConsumerなどの標準ライブラリに追加するには、新しいコンシューマが必要になることに注意してください。現在、JLSはJava.langパッケージ以外の型を参照することはほとんどありません(Java.util.Iteratorなどのいくつかの顕著な例外があります)。これは、安定性層を意味します。提案された変更は、Java言語にはあまりにも劇的です。

また、現在、配列に対して直接呼び出すことができるメソッド(およびJava.lang.Objectとは異なる実装)があることに注意してください。それはclone()メソッドです。実際には、どこでも特別に処理する必要があるため、いくつかのダーティな部分がjavacおよびJVMに追加されます。これによりバグが発生します(たとえば、メソッド参照がJava 8 JDK-8056051で誤って処理されました)。同様の複雑さをjavacに追加すると、さらに類似したバグが発生する可能性があります。

このような機能は、おそらく Arrays 2.0イニシアチブ の一部として、それほど近い将来には実装されないでしょう。アイデアは、クラスライブラリに配置される配列のスーパークラスを導入することです。そのため、javac/JVMを調整せずに通常のJavaコードを記述するだけで新しいメソッドを追加できます。しかし、配列は常にJavaで特別に扱われるため、これは非常に難しい機能でもあり、私が知る限り、実装されるかどうか、いつ実装されるかはまだ不明です。

7
Tagir Valeev