#ソースコード
package test; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class Sub{ public static void main(String[] args) { // List<Integer> list=Arrays.asList(1,2,3,4,5); // list.stream().filter(i->i%2==0).forEach(i->System.out.println(i)); List<Integer> list=Arrays.asList(1,2,3,4,5); Stream<Integer> stream=list.stream(); Stream<Integer> stream2=stream.filter(i->i%2==0); stream2.forEach(i->System.out.println(i)); } }
実行結果
2 4
#概要
JavaのStreamAPIの内部的な動きが知りたかったので、上記ソースコードでコメントアウトしているstreamを分解して、一つ一つEclipseのデバッグ機能でソースコードを辿っていきました。
すると、一ヶ所、腑に落ちない部分がありました。
まず、ArraysクラスのasListメソッドを呼び出し、そのの戻り値であるArrayList<Integer>クラスのインスタンスをList<Integer>型のlist変数に代入します。
その次に、そのlist変数を参照して、streamメソッドを呼び出しています。
この部分をeclipseでブレークポイントに設定し、デバッグ機能を使うと、下記メソッドが呼び出されていました。
public interface Collection<E> extends Iterable<E> { : : default Stream<E> stream() { return StreamSupport.stream(spliterator(), false); } : : }
処理として、StreamSupportクラスのstreamメソッドの第一引数として、spliteratorメソッドの戻り値が第一引数として引き渡されています。
このメソッドはListインターフェース、ArrayListクラス、それぞれで宣言されています。
List<Integer>型のlist変数には、ArrayListインスタンスが代入されているので、オーバライドの優先順位の規則から、ArrayListインスタンスのspliteratorメソッドが呼び出されると思っていました。
しかし、実際に辿ってみると、Arraysクラスのspliteratorメソッドが呼び出されていました。
ArraysクラスとCollectionインターフェース・Listインターフェース・ArrayListクラス、それぞれ継承関係も実装関係もありません。そのため、List型の変数に代入されいてるArrayListインスタンスのメソッド内にて、参照先を指定しない方法でのメソッド呼び出し(spliterator())で、Arraysクラスのメソッドが呼び出される可能性はありません。
(なぜなら、非staticなメソッド内で参照先を指定しないメソッド呼び出しをした場合、自インスタンスのメソッドが呼び出されることになるから。)
public class Arrays { : : public Spliterator<E> spliterator() { return Spliterators.spliterator(a, Spliterator.ORDERED); } : : }
また、ArraysクラスのasListメソッドの内部的な処理でArrayListクラスのインスタンスが生成されていることも確認済みのため、今回のような状況でArraysクラスのspliteratorメソッドが呼ばれる理由が分かりません。
public class Arrays { : : public static <T> List<T> asList(T... a) { return new ArrayList<>(a); } : : }
また、最初のソースコードのArrayListインスタンス化を下記のように変更して、同じようにデバッグ機能を用いると、ちゃんとArrayListクラスのspliteratorメソッドが呼び出されていました。
package test; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; public class Sub{ public static void main(String[] args) { List<Integer> list=new ArrayList<Integer>(); list.add(1); list.add(2); list.add(3); list.add(4); list.add(5); Stream<Integer> stream=list.stream(); Stream<Integer> stream2=stream.filter(i->i%2==0); stream2.forEach(i->System.out.println(i)); } }
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { : : public Spliterator<E> spliterator() { return new ArrayListSpliterator<>(this, 0, -1, 0); } : : }
個人的には、Eclipseのデバッグ機能はソースコードを逆コンパイル?によってclassファイルから無理やり作っているものなので、その不備かと思っているのですが、
Javaの文法的に説明できる方がいましたら、ご回答いただきたいです。
よろしくお願いします。
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/11/29 05:59
2020/11/29 06:10
2020/11/29 06:12
2020/12/01 14:06