Java - Error, Exception, Stream, Record

chrkb1569·2022년 10월 18일

오늘은 Java를 사용하면서 자주 볼 수 있었던 Error와 Exception, Stream API에 대하여 알아본 뒤, Record라는 문법에 대해서 알아보았습니다.

Error & Exception

문법적으로 올바르지 않거나, 논리적으로 올바르지 않은 경우가 발생하였을 경우, 사용자에게 어떠한 문제가 발생하였는지 알려주는 것을 Error라고합니다.

자바는 사용자에게 어떠한 Error가 발생하였는지 표기해주어 사용자가 오류를 수정할 수 있게끔 유도해줍니다.

문법적으로 올바르지 않아서 발생하는 오류를 컴파일 오류, 논리적인 오류를 런타임 오류라고 하는데, 에러는 크게보면 이렇게 2가지로 분류될 수 있을 것 같습니다.

컴파일 오류

소스코드를 바이트코드로 변환하는 과정에서 발생하는 오류로, 문법적인 오류가 발생하여 컴파일러가 잡아주는 오류입니다.

런타임 오류

런타임 오류의 경우에는 문법적으로 잘못된 부분이 없어, 컴파일러가 넘어갔지만 사용자가 입력한 값이나 설계한 로직에 따라서 오류가 발생하는 경우입니다.

자바에서는 오류를 오류(Error)예외(Exception)으로 나눕니다.

Error 클래스와 Exception 클래스 모두 Throwable 클래스를 상속받는다는 공통점이 존재하지만, 심각도 측면에서 차이점이 존재합니다.

오류(Error)

자바에서 Error 클래스를 상속하는 오류는 시스템이 종료되어야할, 매우 심각한 문제를 의미합니다.

JVM에 문제가 발생한 ‘VirtualMachineError’, 입출력관련하여 코드 수준 복구가 불가능한 오류가 발생한 ‘IOError’와 같이 사용자가 처리할 수 없는 예외 상황을 의미하기때문에 그냥 프로그램을 종료시킨 뒤에 원인을 파악해야합니다.

예외(Exception)

Exception 클래스를 상속하는 오류들은 또다시 2가지로 나뉘는데, Exception 클래스를 상속하고 있는 RunTimeException 클래스를 상속하는 클래스와 상속하지 않는 클래스로 나뉩니다.

  • RuntimeException을 상속하는 예외 클래스 우리가 자주 접하는 예외 상황들로, 프로그래머가 예외 처리를 해야하기보다는 코드 오류로 인하여 발생하는 경우가 많기 때문에, 코드를 수정함으로써 해결할 수 있습니다.
  • RuntimeException을 상속하지 않는 예외 클래스 Exception 클래스만 상속받는 예외 클래스로, 다른 예외 클래스들과의 차이점은 try ~ catch문으로 처리해야한다는 점입니다.

Error 클래스를 상속하거나, RuntimeException 클래스를 상속하는 오류들의 경우에는 코드 작성 과정에서 특별히 무엇인가를 하지 않아도 상관없지만, Exception만을 상속하는 클래스의 경우에는 try ~ catch 와 같은 오류 처리 구문을 통하여 예외의 처리를 다른 영역으로 넘기는 코드를 작성해야합니다.

이처럼, 컴파일하는 과정에서 예외 처리 코드가 필요한 예외를 일반 예외(Exception) 혹은 checked Exception이라고 부르며, 컴파일하는 과정에서 예외 처리 코드를 검사하지 않는 예외를 unchecked Exception 혹은 실행 예외(RuntimeException)라고 부릅니다.

Stream API

Stream을 활용하여 Collection, 배열 등의 저장 요소들을 하나씩 참조한 뒤, 반복적으로 처리하는 것을 의미합니다.

반복 순회를 위한 for문, 필터링을 위한 if문을 생략할 수 있어, 불필요한 코딩을 걷어낼 수 있고, 가독성이 향상된다는 장점이 존재합니다.

Stream 구문은 크게 3단계로 이루어지며,

1) 생성

2) 중개 연산

3) 최종 연산

으로 이루어집니다.

스트림 생성

Stream의 경우, 위에서 설명하였던 것처럼 Collection이나 배열과 함께 자주 사용되는데, 배열의 경우에는 Arrays.stream()을 통하여, Collection 인스턴스의 경우에는 stream()과 같은 명령어를 통하여 Stream을 생성할 수 있습니다.

중개 연산

스트림이 생성되었다면, 스트림에 속하는 원소들에 하나하나 연산을 진행하여 값을 변화시키거나, 원하지 않는 값을 제외시킬 수 있습니다.

Ex) filter, map, distinct 등

최종 연산

앞서 함수를 적용하였던 스트림에 있는 요소들에 대하여 최종 연산을 진행함으로써 결과를 도출해냅니다.

Ex) count, min, max, sum, collect 등

Stream의 특징

1) Stream의 경우, 재사용이 불가능하다

2) 병렬 스트림은 여러 개의 스레드가 작업한다.

스트림 생성시, stream()을 사용하지 않고 parallelStream()을 통하여 병렬 처리를 활용하는 스트림을 생성할 수 있는데, 하나의 스레드를 사용하는 방식보다 유리해보일 수 있지만 프로그램에서 활용하는 스레드가 많거나, 스트림의 요소 수가 많지 않을 경우에는 오히려 스레드를 사용하는데에 필요한 리소스가 더 클 수 있으므로, 무조건 사용하는 것은 삼가해야합니다.

3) 중개 연산은 최종 연산이 존재해야 작업을 수행한다.

스트림 생성 코드와 중개 연산 코드가 존재하더라도, 최종 연산 코드가 존재하지 않는다면 Stream은 작업을 수행하지 않습니다. 이러한 특징은 미리 계산해놓는 방식과 비교하였을 때, 두 번 순회하는 작업을 막을 수 있습니다.

4) 원본의 데이터를 변경하지 않는다.

원본의 데이터를 조회하여 원본의 데이터가 아닌 요소들로 Stream을 생성하기 때문에, 정렬이나 필터링 등의 작업은 별도의 Stream 요소들로부터 처리가 됩니다.

Record

불변의 데이터 객체를 쉽게 관리할 수 있도록 하는 새로운 유형의 클래스로, JDK16에서부터 사용되는 방식입니다.

기존의 자바 문법에서 데이터를 저장하기 위한 객체를 생성할 경우, 다음과 같이 생성하여 관리하였을 것입니다.

기존의 데이터 객체 Person

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

일단 대략적으로 getter와 setter만을 객체 내부에 정의해주었는데, 해당 객체가 어떻게 활용되느냐에 따라서 별도의 메소드를 추가해주어야할 것입니다.

예를 들어 객체를 출력해야할 경우, toString() 메소드를 정의해주어야 할것이고, 경우에 따라서는 equals() 메소드나 hashCode() 등의 메소드를 정의해주어야 할 것입니다.

이러한 방식을 활용할 경우, 하나하나 관리해주어야하기 때문에 많이 불편합니다.

따라서 자바에서는 전보다 불변의 객체를 편하게 관리할 수 있도록 record 클래스를 제공하였습니다.

record 클래스를 활용한 객체 관리

public record Person(String name, int age){
}

다음과 같이 선언함으로써, 컴파일러는 헤더를 통하여 내부 필드를 추론하여 생성자, toString, equals, hashCode 메소드에 대한 구현을 자동으로 제공해줍니다.

따라서, 이와 같이 record 클래스를 통하여 클래스의 관리를 보다 더 쉽게 할 수 있으며, 내부에 다음과 같은 제약 조건을 걸어서 원하지 않는 값이 입력되었을 경우 오류를 반환하도록 설정할 수 있습니다.

나이가 음수인 경우, 오류 반환

public record Person(String name, int age) {
    public Person{
        if(age < 0) {
            throw new UserCustomException(””);
        }
    }
}

0개의 댓글