私が最近あるコードレビューをしていてなるほどと思ったコードがありちょっと試してみたことの共有です。
Stream APIの map()
map
<R> Stream<R> map(Function<? super T,? extends R> mapper)
Returns a stream consisting of the results of applying the given function to the elements of this stream.
This is an intermediate operation.
Type Parameters:
R - The element type of the new stream
Parameters:
mapper - a non-interfering, stateless function to apply to each element
Returns:
the new stream
ここに書かれている通りです。 applying the given function to the elements of this stream
ということで、引数に指定した関数をこのストリームの要素たちに行います。
気づき
List<HogeHoge> entityList = ...;
result = entityList.stream() //
.map(HogeHogeDto::new) //
.sorted() //
.collect(Collectors.toList());
どうなるほどと思ったかというと、これで entityList
の中身が HogeHogeDto
のコンストラクタ、具体的には HogeHogeDto(HogeHoge)
を呼び出している点です。上記ソースコードでは HogeHogeDto::new
としか書いていないのでどのコンストラクタを呼び出すかまでは明示していませんが、型推論が働いてちゃんと HogeHogeDto(HogeHoge)
を呼び出してくれます。
検証
HogeHogeDto
クラスに、引数に HogeHoge
を取るコンストラクタと、デフォルトコンストラクタがあった場合、ちゃんと HogeHoge
を取るコンストラクタが呼ばれるか?
→
追加検証
では、デフォルトコンストラクタの方を明示的に呼びたい(ストリームで渡されるものは無視したい)時はどうすればよいのでしょうか?
→明示的に無視する書き方をすることでできる。
一方で、デフォルトコンストラクタの方を呼びたい場合、https://t.co/Z6Ycvao6kR().map(e -> new Hoge()) とすればよい。へぇへぇ。これを単に .map(new Hoge()) とするのはコンパイルエラー。
— fukushiw (@fukushiw) 2017年7月31日
検証コード
import java.util.List;
import java.util.ArrayList;
/**
* Stream API 基礎トレーニング。
* {@code map()} を使用した新しいリストの生成。
*/
public class StreamMapTest {
/**
* エントリポイント。
* 変換前のリスト {@code List<Integer>} を元に、そのリストのサイズの分だけ
* {@code StreamMapTest} のインスタンスを生成し、それぞれに所定の処理を行う。
*
* @param arguments 使用しない
*/
public static void main(String[] arguments) {
// テスト用に使用する変換前のリスト。
// ここでは、StreamTest#value が int 型なので、後で代入
// しやすくするよう List の中身は Integer としている。
// なお0はデフォルトコンストラクタで使用するため1から
// 値を代入している。
List<Integer> values = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
values.add(new Integer(i));
}
//以下確認テスト
//(違いを見やすくするため個別のメソッドにせず
// main()メソッド内に列挙している)
// 1:intを引数に取るコンストラクタが呼ばれるパターン(1)
System.out.println("test1------");
values.stream() //
.map(StreamMapTest::new) //
.forEach(e -> System.out.println(e.getValue()));
// 2:intを引数に取るコンストラクタが呼ばれるパターン(2)
System.out.println("test2------");
values.stream() //
.map(e -> new StreamMapTest(e)) //
.forEach(e -> System.out.println(e.getValue()));
// 3:デフォルトコンストラクタが呼ばれるパターン
System.out.println("test3------");
values.stream() //
.map(e -> new StreamMapTest()) //
.forEach(e -> System.out.println(e.getValue()));
// 4:コンパイルエラーになるパターン
//System.out.println("test4------");
//values.stream() //
// .map(new StreamMethodTest()) //
// .forEach(e -> System.out.println(e.getValue()));
}
/** どのようにインスタンスが作られたかをわかりやすくするために保持する値。 */
private final int value;
/**
* デフォルトコンストラクタ。
* インスタンス変数の値には0を設定している。
*/
public StreamMapTest() {
this(0);
}
/**
* intの引数を1つ取るコンストラクタ。
* 指定された値をインスタンス変数に代入している。
*
* @param value インスタンス変数に代入する値
*/
public StreamMapTest(int value) {
this.value = value;
}
/**
* インスタンス変数の値を取得する。
*
* @return インスタンス変数の値
*/
public int getValue() {
return value;
}
}
実行結果
C:\TEMP>java StreamMapTest
test1------
1
2
3
test2------
1
2
3
test3------
0
0
0
C:\TEMP>