코딩을 하다 보면, 배열 또는 List 형태가 아닌 데이터를 List 데이터 타입으로 만들 때 간편하게 Arrays.asLIst 메서드를 활용해서 그동안 리스트 데이터를 만들고 있었다.
List<Integer> score1 = Arrays.asList(1, 2, 3, 4, 5);
그러다가 Arrays.asList로 만든 List A에서 DB에서 조회한 List B의 값을 빼야 하는 경우가 생겼고 아무 생각 없이 다음과
같이 코드를 작성해봤다.
List<Integer> score1 = Arrays.asList(1, 2, 3, 4, 5);
// DB에서 값을 가져옴
List<Integer> score2 = getSomeDataFromDatabase();
// DB 값을 제외한 나머지 값을 찾아보자!
score1.removeAll(score2);
그랬더니, UnsupportedOperationException 예외가 발생했다. 한마디로 해당 연산은 지원하지 않는다는 에러이다.
removeAll과 같은 메서드는 List 인터페이스를 구현한 구현체에 모두 적용되는데, 같은 ArrayList인데 왜 해당 에러가 발생할까?라는 생각이 들어 Arrays.asList의 메서드 코드를 살펴보았다.
이 부분을 보고 약간 충격을 받았다. 리턴 타입 클래스명이 ArrayList이고, List 타입 변수에 할당이 잘 되기에 평소에 하는 java.util 패키지에 있는 ArrayList 클래스를 리턴할 줄 알았는데, Arrays 클래스의 inner class인 ArrayList를 리턴하는 것이었다.
Arrays.asLIst의 메서드 설명 부분을 보면, java.util 패키지에 있는 ArrayList와 확연한 차이를 알 수 있는데 바로 Arrays.asLIst로 리턴되는 java.util.Arrays.ArrayList는 고정크기의 배열을 가진 리스트를 리턴한다는 것이었다. 따라서 크기가 변경되는 동작인 remove, removeAll, add 등의 연산이 지원되지 않는 것이었다.
이를 확인해보기 위해 다음과 같이 테스트 코드를 작성해봤다.
package com.tistory.johnmark;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
class ArraysOperationTest {
@Test
@DisplayName("Arrays removeAll 테스트")
void arraysRemoveAll() {
// given
List<Integer> score1 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> score2 = new ArrayList<>();
score2.add(1);
score2.add(2);
try {
// when
score1.removeAll(score2);
} catch (Exception e) {
System.out.println("----------------------------------");
System.out.println("ArraysOperationTest.arraysRemoveAll");
System.out.println("Exception = " + e);
// then
Assertions.assertThat(e).isInstanceOf(UnsupportedOperationException.class);
}
}
@Test
@DisplayName("Arrays add 테스트")
void arraysAddTest() {
// given
List<Integer> score = Arrays.asList(1, 2, 3, 4, 5);
try {
// when
score.add(6);
} catch (Exception e) {
System.out.println("----------------------------------");
System.out.println("ArraysOperationTest.arraysAddTest");
System.out.println("Exception = " + e);
// then
Assertions.assertThat(e).isInstanceOf(UnsupportedOperationException.class);
}
}
@Test
@DisplayName("Arrays remove 테스트")
void arraysRemoveTest() {
// given
List<Integer> score = Arrays.asList(1, 2, 3, 4, 5);
try {
// when
score.remove(5);
} catch (Exception e) {
System.out.println("----------------------------------");
System.out.println("ArraysOperationTest.arraysRemoveTest");
System.out.println("Exception = " + e);
// then
Assertions.assertThat(e).isInstanceOf(UnsupportedOperationException.class);
}
}
}
데이터 크기가 변경하는 연산을 수행했을 때, 반드시 Exception이 발생되고, 해당 Exception은 UnsupportedOperationExcception이여야 테스트를 통과하는 코드이다.
테스트를 수행한 결과는 다음과 같다.
이번 계기를 통해, 내가 사용하는 코드가 정확한 어떤 코드인지 어떤 데이터를 리턴하고 해당 데이터의 타입은 무엇인지 확인을 잘해야 한다는 생각이 들었고, 기본기에 대한 중요성을 다시 한번 깨닫게 되었다.
※ UnsupportedOperationException을 회피하는 방법
List<Integer> score = new ArrayList<>(Arrays.asList(1,2,3));
간단하게 위와 같이 ArrayList로 wrapping 하면 된다.
'Programming > Java' 카테고리의 다른 글
[인프런/호주 현직 자바 개발자가 묻고 답하는 영어 기술 면접] 강의 정리 (0) | 2021.12.26 |
---|---|
[백 투 더 베이직] 객체지향 SOLID 원칙 (0) | 2021.07.19 |
[Java8] 함수형 인터페이스와 람다 표현식 (0) | 2021.06.26 |
객체, 클래스 그리고 인스턴스 (0) | 2021.06.15 |
[Java] CMD 또는 Shell 환경에서 Java 컴파일 및 Jar 파일 만들기 (0) | 2021.01.12 |