AbstractList.add(E)는 쓸 수 없어!
개발 중에 list.addAll(T);
와 같은 코드 작성할 일이 있었다. 하지만 계속 java.lang.UnsupportedOperationException
에러가 나는 게 아닌가? 결론부터 말하자면 생각 없이 Collections.emptyList();
로 초기화했기 때문이다.
java.lang.UnsupportedOperationException
java 문서에는 이 Exception을 이렇게 설명한다.
- 요청한 오퍼레이션이 지원되지 않는 경우 Exception 발생
- UnsupportedOperationException는 Java Collection Framework에 Exception 클래스다.
그럼 Java Collection Framework를 사용하는 경우 비정상 요청을 한 경우 throw 되는 것인가??
Collections.emptyList()
아래 코드는 emptyList 메소드다. 단순히 EMPTY_LIST라는 멤버 변수를 반환 하는데, 주석에 immutable한 empty list를 반환
한다고 쓰여있다.
/**
* Returns an empty list (immutable). This list is serializable.
*
* <p>This example illustrates the type-safe way to obtain an empty list:
* <pre>
* List<String> s = Collections.emptyList();
* </pre>
*
* @implNote
* Implementations of this method need not create a separate <tt>List</tt>
* object for each call. Using this method is likely to have comparable
* cost to using the like-named field. (Unlike this method, the field does
* not provide type safety.)
*
* @param <T> type of elements, if there were any, in the list
* @return an empty immutable list
*
* @see #EMPTY_LIST
* @since 1.5
*/
@SuppressWarnings("unchecked")
public static final <T> List<T> emptyList() {
return (List<T>) EMPTY_LIST;
}
EMPTY_LIST 멤버 변수는 public static final List EMPTY_LIST = new EmptyList<>();
EmptyList 클래스라는 놈으로 초기화하고 있다.
EmptyList
아래 코드가 바로 EmptyList 클래스다. ArrayList
와 마찬가지로 AbstractList
를 상속하고 있다. add()
와 addAll()
메소드가 없는 것을 확인할 수 있다. 왜냐하면, immutable 한 list니까!
/**
* @serial include
*/
private static class EmptyList<E>
extends AbstractList<E>
implements RandomAccess, Serializable {
private static final long serialVersionUID = 8842843931221139166L;
public Iterator<E> iterator() {
return emptyIterator();
}
public ListIterator<E> listIterator() {
return emptyListIterator();
}
public int size() {return 0;}
public boolean isEmpty() {return true;}
public boolean contains(Object obj) {return false;}
public boolean containsAll(Collection<?> c) { return c.isEmpty(); }
public Object[] toArray() { return new Object[0]; }
public <T> T[] toArray(T[] a) {
if (a.length > 0)
a[0] = null;
return a;
}
public E get(int index) {
throw new IndexOutOfBoundsException("Index: "+index);
}
public boolean equals(Object o) {
return (o instanceof List) && ((List<?>)o).isEmpty();
}
public int hashCode() { return 1; }
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
return false;
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
}
@Override
public void sort(Comparator<? super E> c) {
}
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
}
@Override
public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
// Preserves singleton property
private Object readResolve() {
return EMPTY_LIST;
}
}
하지만 EMPTY_LIST.addAll() 호출이 가능하다.
Collections.emptyList();
메소드로 생성한 List는 ArrayList
가 아니다. AbstractList
라는 같은 부모를 바라보고 있지만, 주석에서 설명하듯 EMPTY_LIST는 immutable한 list이기 때문에 add()
, addAll()
메소드는 AbstractList
에 메소드가 호출된다.
AbstractList.add(), addAll()
addAll()
메소드는 결국 아래 보이는 add(int, E)
메소드를 호출하는데, 여기서 바로 UnsupportedOperationException
를 던진다.
/**
* {@inheritDoc}
*
* <p>This implementation always throws an
* {@code UnsupportedOperationException}.
*
* @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
* @throws IllegalArgumentException {@inheritDoc}
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
Collections.emptyList() 만 그럴까?
아마 Arrays.asList(T...)
메소드도 많이 사용할 것이다. 이 메소드 또한 add(E)
메소드를 사용할 수 없다.
테스트
public class CollectionTests {
@Test(expected = UnsupportedOperationException.class)
public void testArraysAdd() throws Exception {
Collection<String> list = Arrays.asList("a", "b", "c");
list.add("d");
fail();
}
@Test(expected = UnsupportedOperationException.class)
public void testEmptyListAdd() throws Exception {
Collection<String> list = Collections.EMPTY_LIST;
list.add("a");
fail();
}
}
결론
생각하고 쓰자.
출처 : https://blog.woniper.net/350?category=506090
'JAVA > Java' 카테고리의 다른 글
[Java] 문자열 비교 equals/equalsIgnoreCase/compareTo (0) | 2021.03.16 |
---|---|
java isNumeric, isDigits, isNumber (0) | 2020.10.27 |
[mybatis] foreach를 이용한 다중 insert (0) | 2020.09.07 |
java inner class (0) | 2020.09.07 |
naver 검색 API 예제 (0) | 2020.09.07 |
java8 stream match (0) | 2020.09.07 |
SuperTypeToken #2 TypeToken(ModelMapper)과 ParameterizedTypeReference(Spring) 차이 (0) | 2020.09.07 |
SuperTypeToken #1 TypeToken(ModelMapper)과 ParameterizedTypeReference(Spring) 사용법 (0) | 2020.09.07 |