글을 쓰게 된 계기
Assertions의 메서드들 중에는 다음과 같이 Exception을 테스트하는 메서드가 있다
<T extends Throwable> ThrowableTypeAssert<T> assertThatExceptionOfType(final Class<? extends T> exceptionType)
NotThrownAssert assertThatNoException()
ThrowableTypeAssert<NullPointerException> assertThatNullPointerException()
ThrowableTypeAssert<IllegalArgumentException> assertThatIllegalArgumentException()
ThrowableTypeAssert<IOException> assertThatIOException()
ThrowableTypeAssert<IllegalStateException> assertThatIllegalStateException()
assertThatExceptionOfType은 예외에 대해 포괄적으로 처리하는 메서드이고,
assertThatNoException은 예외가 발생하지 않는지 확인하는 메서드이다.
그리고 나머지 4개는 자주 사용하는 Exception에 대해 편의를 위해 만들어둔 메서드라고 한다.
자주 사용한다고 하니 이후에 만날 수도 있을 것 같았다. 그래서 이번 기회에 확실히 알아두면 좋을 것 같아 4가지의 예외를 정리해보려고 한다!
1. NullPointerException
public class NullPointerException extends RuntimeException {
...
}
예외의 이름을 보면 알 수 있듯이, null을 참조할 때 발생할 수 있는 예외이다.
이 경우 컴파일 시에는 에러가 발생하지 않고 Runtime에 발생한다.
void getNumberBetween1To10(int number) {
List<Integer> numbers = new ArrayList<>();
if (number == 1) {
numbers = null;
}
numbers.get(1);
}
다음과 같이 Runtime에 null이 될 수 있는 것을 참조한다면 null을 참조하였다는 예외가 발생할 수 있다.
(이 경우 컴파일 시에 잡아주지 않는다)
2. IllegalArgumentException
public class IllegalArgumentException extends RuntimeException {
...
}
IllegalArgumentException 또한 런타임에 발생할 수 있는 에러다.
적합하지 않거나, 적절하지 못한 인자를 메소드에 넘겨주었을 때 발생한다.
예를 들면 다음과 같은 상황에 발생한다
void getNumberBetween1To10(int number) {
if (number < 1 || number > 10) {
throw new IllegalArgumentException("1과 10 사이 숫자여야합니다");
}
}
1과 10 사이 값을 받아야하는데, 인자 단에서 확인할 방법이 없다.
그래서 메서드 안에서 검증을 수행하고, 적절한 값이 아닐 경우 IllegalArgumentException을 던진다.
3. IOException
class IOException extends Exception {
...
}
백준에서 BufferedReader를 많이 사용해본 사람이라면 자주 봤을 예외이다.
이 클래스는 실패하거나 중단된 I/O 작업으로 인해 발생하는 일반적인 예외 클래스입니다.
I/O가 실패할 경우에 발생할 수 있는 Exception이라고 한다. 사용자에게 입력을 받거나 출력할 때 발생할 수 있다.
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.close();
br.readLine();
}
다음과 같이 BufferedReader를 사용할 때는 IOException을 무조건 처리되어야한다.
중간에 BufferedReader가 닫힌상황에서 사용자의 입력을 읽으려고 할 때 IOException이 발생하게 된다.
4. IllegalStateException
public class IllegalStateException extends RuntimeException {
...
}
이 예외도 Runtime에 발생할 수 있는 예외이다.
설명을 읽어보면 다음과 같이 말한다
불법적이거나 부적절한 시간에 메서드가 호출되었음을 나타냅니다. 즉, Java 환경이나 Java 애플리케이션이 요청된 작업에 적합한 상태가 아닙니다.
메소드가 요구된 처리를 하기에 적합한 상태가 아닐 때 발생하는 예외라는데, 발생할 수 있는 상황을 보면서 어떤 예외인지 감을 잡아보자!
(1) Spring: Controller의 @RequestMapping의 값이 중복될 경우
@GetMapping("info")
public void test1() {}
@GetMapping("info")
public void test2() {}
(스프링부트에서 Controller로 요청을 받는 경우)
이 경우는 Controller의 RequestMapping이 동일한 2가지 경우가 있기 때문에 적절한 메서드를 찾을 수 없어 IllegalStateException이 발생한다고 한다.
이 상황을 보면 IllegalStateException은 input값으로 적절한 메서드를 찾을 수 없을 때 발생한다고 생각할 수 있다.
(2) 안드로이드에서 Fragment전환이 발생할 때
onSaveInstanceState()이 호출된 이후 Fragment 전환이 발생한다면, onSaveInstanceState()를 총해 결정되는 복구 시점과 다르게에 FragmentTransction에 대해서 복구할 수가 없다고 한다. 이런 상황이 발생한다면 사용자 경험(UX)를 해치는 결과를 초래하기 때문에 IllegalStateException을 던진다고 한다.
안드로이드에 대해 잘 모르지만, 맥락만 이해해본다면 도중에 상태가 바뀌어 원하는 결과값을 내놓을 수 없는 상황일 때 예외가 발생하게된다고 한다.
메서드를 수행하는 도중 상태가 변하여 적절한 결과값을 내놓을 수 없을 때 발생한다고 이해하면 될 것 같다.
(3) 자바 컴파일러가 2개의 다른 패키지에서 같은 이름의 클래스를 2개이상 찾을 때 발생
프로젝트를 임포트하고, 한 번에 두 클래스를 모두 가져올 때 해당하는 클래스를 찾을 수 없어 IllegalStateException이 발생한다고 한다.
(직접 해봤는데, 패키지가 달라도 클래스명이 중복되는 게 있으면 IDE에서 처리를 해줬다)
흔히 발생하는 상황은 아닌 것 같다.
이를 통해 IllegalStateException은 같은 이름의 클래스가 2개 있어 어떤 걸 선택해야하는지 모르는 상황에 발생하는 것을 알 수 있다.
(4) 동일한 ResourceReslover에서 다중 스레드를 사용했거나, 코드에서 열지않은 ResourceResolver 또는 javax.jcr.Session을 닫은 경우 발생
리소스를 동시에 변경하거나, 열지않은 걸 닫으려고 할 때 발생하는 것 같다.
(5) Steam<Object> 타입을 Map<String, Object>로 변환하는 과정에서 발생
class Student {
private String name;
public Student(String name) { this.name = name; }
public String getName() { return name; }
}
public class Main {
public static void main(String[] args) {
Student[] arr = {
new Student("연지"),
new Student("연지"),
new Student("마루"),
new Student("망곰")
};
Map<String, Student> map = Stream.of(arr)
.collect(Collectors.toMap(s -> s.getName(), p->p));
}
}
참고한 예시블로그: https://bada744.tistory.com/87
다음과 같이 stream을 사용하여 Map에 매핑할 때 key가 중복되는 경우 IllegalStateException이 발생한다.
이를 통해 IllegalStateException은 메서드를 수행할 때 정상적으로 동작할 수 없는 상황이라면 해당 예외를 던지는 것을 알 수 있다
정리해보면 IllegalStateException은 다음과 같은 상황에 발생한다
- 입력에 대해 2가지 동작이 수행될 수 있고, 어떤 걸 선택해야할 지 모를 때
- 메서드를 수행하는 도중 상태가 변하여 메서드가 적절한 결과값을 내놓을 수 없을 때
- 리소스를 동시에 변경하여 상태가 변경될 때
- 메서드를 수행할 때 입력값으로 정상적인 동작을 수행할 수 없을 때
그 외에도 부적절한 상황에 메서드가 호출될 때 사용될 수 있을 것 같다.
아직 IllegalArgumentException과 IllegalStateException이 조금 헷갈리지만, 맥락에 따라 잘 사용하다보면 익숙해질 수 있을 것 같다.
참고자료
https://goddaehee.tistory.com/126
https://help.acmicpc.net/judge/rte/IllegalArgumentException
https://it-hhhj2.tistory.com/95
https://readystory.tistory.com/113
https://bada744.tistory.com/87
'프로그래밍 언어 > Java' 카테고리의 다른 글
IntelliJ java.util.concurrent.TimeUnit 이 없다고 나오는 에러(JDK21) (3) | 2024.10.26 |
---|---|
[JAVA] Test로 Collections 공부하기 (0) | 2023.11.07 |
[JAVA] 상수와 enum 공부하기2 (0) | 2023.11.07 |
[JAVA] 상수와 enum 공부하기1 (0) | 2023.11.07 |
[JAVA] 상수를 private static final로 선언하는 이유 (0) | 2023.11.07 |