본문 바로가기
☕️ JAVA/🦋 Effective Java

표준 예외를 사용하자

by kukim 2022. 2. 6.

이 글은 책 이펙티브 자바 3판 Item72과 하단 Reference 참고했습니다. 잘못된 내용이 있다면 편하게 말씀해주세요 🙏🏻

 

결론

자바 언어가 제공하는 표준 예외는 충분히 많다.

직접 만들지 말고 현재 상황에 맞는 표준 예외를 찾아 사용하자


이전 글 표준 라이브러리를 익히고 사용하자 에서 표준 라이브러리를 써야 하는 이유에 대해 알아봤다.

예외를 던질때도 마찬가지이다. 표준 예외를 사용하자.

왜?

  • 읽기 쉬워진다.
  • 메모리 사용량 감소, 클래스 적재 시간 감소, 예외도 직렬화에 포함되기 때문에 표준 사용이 좋다

자주 쓰이는 예외

예외 쓰임
IllegalArgumentException 허용하지 않는 값이 인수로 건네졌을 때 (null일 때 써도 되지만 따로) 나이를 나타내는 매개변수에 음수가 들어올 때
IllegalStateException 객체가 메서드 수행하기에 적절하지 않은 상태일 때 제대로 초기화되지 않은 객체를 사용할 때
NullPointerException 인수가 null일 때 null값을 허용하지 않는 메서드에 null을 건넨 경우
IndexOutOfBoundsException 시퀀스의 범위를 넘어섰을 때  
ConcurrentModificationException 허용하지 않는 동시 수정이 발견됐을 때 단일 스레드에서 사용하려고 설계한 객체를 여러 스레드가 동시에 수정하려 할 때
UnsupportedOperationException 호출한 메서드를 지원하지 않을 때 구현하려는 인터페이스의 메서드 일부를 구현할 수 없을 때 사용 List 구현체에 누가 remove 메서드를 호출할 때 던짐

 

단, 상위 예외 클래스(Exception, RuntimeException, Throwable, Error) 직접 사용하지 말자

상위 예외 클래스를 사용한다면, 예외 발생 시 구체적인 정보를 알 수 없다.


예제

IllegalArgumentException

허용하지 않는 값이 인수로 전달되었을 때의 경우 예외처리로 사용된다.

public class Person {

    private int age;

    public Person(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("age는 음수를 허용하지 않습니다.");
        }
        this.age = age;
    }

    public static void main(String[] args) {
        Person test = new Person(-3);
    }
}

NullPointerException

인수가 null일 때

public class Person {
    private Integer age;

    public Person(Integer age) {
        if (Objects.isNull(age)) {
            throw new NullPointerException("age가 null 입니다.");
        }
        this.age = age;
    }

    public static void main(String[] args) {
        Person test = new Person(null);
    }
}

IndexOutOfBoundsException

시퀀스의 범위를 넘어섰을 때

public class CustomList {
    List<Integer> list = new ArrayList<>();

    public void add(Integer number){
        if(list.size() > 4){
            throw new IndexOutOfBoundsException("최대 저장 개수를 초과했습니다.");
        }
        System.out.println(list.size());
        list.add(number);
    }
    public static void main(String[] args) {
        CustomList list = new CustomList();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6); // IndexOutOfBoundsException 발생
    }
}

UnsupportedOperationException

호출한 메서드를 지원하지 않을 때 예외를 발생시킨다.

아래 메인문 코드는 아무 문제없어 보이지만 UnsupportedOperationException예외가 발생된다.

public class UnsupportedOperationTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3);
        list.add(10);
    }
}
Exception in thread "main" java.lang.UnsupportedOperationException // 예외 발생
	at java.base/java.util.AbstractList.add(AbstractList.java:153)

이유는 Arrays.asList()의 결과로 리턴된 list에 add() 메서드가 구현되어있지 않기 때문이다.

좀 더 자세히 보자면 정적 메서드 Arrays.asList()의 리턴 값은 Arrays 내부 inner class로 구현되어있는 ArrayList를 리턴한다. 이는 java.util.ArrayList와는 다르다. Arrays 내부 inner class의 ArrayList는 add, remove 등이 구현되어있지 않다. 따라서 List 인터페이스에서 add()가 오버 라이딩되어있지 않아 UnsupportedOperationTest 예외를 발생시킨다.

// 정정 메서드 Arrays.asList()의 소스코드
public static <T> List<T> asList(T... a) {
 // 리턴 값 : ArrayList 이지만 java.util.ArrayList와 동일하지 않음, 아래 inner class 이다.
        return new ArrayList<>(a);
    }

    /**
     * @serial include
     */
    private static class ArrayList<E> extends AbstractList<E>
        implements RandomAccess, java.io.Serializable
    {
			// 내부 inner class 구현체 (생략)
			// size, toArray, get, contains, indexOf, sort, iterator replaceAll 메서드만 오버라이딩 되어있다
		}

⛓ Reference

https://stackoverflow.com/questions/5755477/java-list-add-unsupportedoperationexception

댓글