0
1

新設されたList生成メソッドを既存のメソッドの代わりに使う場合の注意点

Posted at

Java9以降で、List生成メソッドがいくつか新設されましたが、既存のList生成メソッドを単純に置き換えると危険な場合があります。

Listを生成するメソッド

Listを生成するメソッドで、よく使うものは以下があります。

// Java1.2から
java.util.ArraysのasList(E...) 

// Java8から
java.util.Streamのcollect(java.util.Collectors.toList())

// Java9から
java.util.Listのof(E...) 

// Java16から
java.util.StreamのtoList()

このうち、Stream.of(...).collect(Collectors.toList())だけは可変リストになっています。

|  JShellへようこそ -- バージョン17.0.9
|  概要については次を入力してください: /help intro

jshell> var arraysAsList = Arrays.asList("ABC")
arraysAsList ==> [ABC]

jshell> arraysAsList.add("XYZ")
|  例外java.lang.UnsupportedOperationException
|        at AbstractList.add (AbstractList.java:153)
|        at AbstractList.add (AbstractList.java:111)
|        at (#2:1)

jshell> var collectorsToList = Stream.of("ABC").collect(Collectors.toList())
collectorsToList ==> [ABC]

jshell> collectorsToList.add("XYZ")
$1 ==> true

jshell> /vars collectorsToList
|    List<String> collectorsToList = [ABC, XYZ]

jshell> var listOf = List.of("ABC")
listOf ==> [ABC]

jshell> listOf.add("XYZ")
|  例外java.lang.UnsupportedOperationException
|        at ImmutableCollections.uoe (ImmutableCollections.java:142)
|        at ImmutableCollections$AbstractImmutableCollection.add (ImmutableCollections.java:147)
|        at (#6:1)

jshell> var streamToList = Stream.of("ABC").toList()
streamToList ==> [ABC]

jshell> streamToList.add("XYZ")
|  例外java.lang.UnsupportedOperationException
|        at ImmutableCollections.uoe (ImmutableCollections.java:142)
|        at ImmutableCollections$AbstractImmutableCollection.add (ImmutableCollections.java:147)
|        at (#8:1)

jshell>

nullを受け付けないList.of()

もう1つ注意したほうが良さそうなのが、これです。Arrays.asList()nullを許容するので、場合によってはArrays.asList()の代わりに使うとまずいことになります。

それらはnull要素を許可しません。 null要素でそれらを作成しようとすると、NullPointerExceptionになります。

これ以外にも注意点はありますが、今回はこの部分だけの紹介に留めます。

List.of()に置き換えて失敗するケース(フィクション)

前述のとおり、List.of()は、Arrays.asList()と異なりnullを受け付けません。そのため、場合によっては単純にArrays.asList()の代わりに使えないかもしれません。

以下の例はフィクションですが、過去に似た事象(言うほど似てないかも)に遭遇したことはあったので、あり得ない話ではないかと思います。
ちなみに、Stream.toList()で作ったリストにadd()するケースは、説明するまでもないですよね。

MemberSearchApiRegistrationApiは、それぞれ別の部署が管轄するAPIで、MemberSearchApiはかなり昔に作られていて使い勝手が微妙なものとします。

/**
 * メンバーを検索するAPI。
 */
public class MemberSearchApi {

	/**
	 * メンバーを検索する。複数指定可能。
	 * 
	 * @param id メンバーID (可変長)
	 * @return keyと同じサイズの配列、対応するメンバーが見つからなかった場合の要素は null
	 */
	public Member[] find(String... memberIds) {
		Member[] results = new Member[memberIds.length];
		for (int i = 0; i < memberIds.length; i++) {
			results[i] = find(memberIds[i]);
		}
		return results;
	}

	private String find(String keyword) {
		// 実物は、何らかのテーブルを検索(当時はMap.ofは使えなかったはずだがそこは無視)
		var table = Map.of("D0010001", Member.named("Alice"), "D0010003", Member.named("Brian"));
		return table.get(id);
	}
}
/**
 * 登録API。
 */
public class RegistrationApi {

	/**
	 * 参加メンバーを登録する。
	 * 
	 * @param memberList メンバーリスト
	 */
	public void joinMembers(List<Member> memberList) {
		// 略
	}
}

この2つのAPIをリレーします。

RegistrationApiは、nullを読み飛ばしてくれるので、Arrays.asList()であれば問題なく動作するようです。

MemberSearchApi memberSearchApi = new MemberSearchApi();
Member[] members = memberSearchApi.find("D0010001", "D0010002", "D0010003");

RegistrationApi registrationApi = new RegistrationApi();
registrationApi.joinMembers(Arrays.asList(members));

// => 正常終了

片や、List.of()では・・・

registrationApi.joinMembers(List.of(members));
Exception in thread "main" java.lang.NullPointerException
	at java.base/java.util.Objects.requireNonNull(Objects.java:208)
	at java.base/java.util.ImmutableCollections.listFromArray(ImmutableCollections.java:190)
	at java.base/java.util.List.of(List.java:1047)
	at ...

おわり

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1