[내배캠/11일차] TIL - 예외, 제네릭, enum, Lv.3 계산기 진행

euphony·2025년 1월 8일
0

내일배움캠프

목록 보기
26/66

✅오늘의 한 일

  • 자바 문법 종합반 4주차 끝내기
  • 자바 중급2 섹션2 정리
  • Lv.3 계산기 시작

💻오늘의 학습

자바 문법 종합반 4주차

오류 및 예외

  • 오류(Error) : 일반적으로 회복이 불가능한 문제. 시스템 레벨에서 또는 환경적인 이유로 발생.
  • 예외(Exception) : 일반적으로 회복이 가능한 문제
    • 체크 예외(Checked Exception) : 컴파일 시점에 확인하는 예외로, 반드시 예외 처리가 필요
    • 언체크 예외(Unchecked Exception) : 런타임 시점에 확인되는 예외로, 예외 처리가 반드시 필요하지 않은 예외

예외는 try - catch - finally 구문을 통해 핸들링할 수 있다.

public class StudyException {
    public static void main(String[] args) {
        OurClass ourClass = new OurClass();

        try {
            ourClass.thisMethodIsDangerous();
        } catch (OurBadException e) {
            System.out.println(e.getMessage());
        } finally {
            System.out.println("우리는 방금 예외를 handling 했습니다!");
        }

    }
}

예외 계층은 다음과 같다. 예전에 다른 강의에서 배웠을 때, 가장 상위 예외는 Throwable이지만 예외를 잡을 때 상위 예외를 잡으면 그 하위 예외까지 잡게 된다고 했다. 따라서 애플리케이션 로직에서 Exception뿐만 아니라 Error 예외까지 함께 잡게 되기 때문에 Exception 부터 필요한 예외로 생각하고 잡아야 한다.

  • 연결된 예외(Chained Exception) : 원인 예외를 새로운 예외에 등록한 후 다시 새로운 예외를 발생시키는데, 이를 예외 연결이라고 한다.
    -> 여러 가지 예외를 하나의 큰 분류의 예외로 묶어서 다루기 위해서.
    -> 체크 예외를 언체크 예외로 포장하기 위해서.

자바 중급 2편 강의

제네릭(Generic)

제네릭(Generic)은 '일반적인', '범용적인'이라는 의미를 가지고 있다. 특정 타입에 속하지 않고 일반적으로 사용할 수 있다는 뜻이다.제네릭을 이용하면 코드 재사용성과 타입 안정성을 모두 충족시킬 수 있다. 다음과 같이 <>(다이아몬드)를 사용한 클래스를 제네릭 클래스라고 한다.

  • 클래스명 오른쪽에 <T> 와 같이 선언한다.
  • T는 타입 매개변수로, 다양한 타입으로 변할 수 있다.
  • 클래스 내부에 T 타입이 필요한 곳에 적어둔다.
  public class GenericBox<T> {
    private T value;

    public void set(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }
}

이렇게 만든 제네릭 클래스는 다음과 같이 원하는 타입을 넣어 객체를 생성해 사용한다.

GenericBox<Integer> integerBox = new GenericBox<Integer>();
GenericBox<String> stringBox = new GenericBox<String>();
GenericBox<Double> doubleBox = new GenericBox<Double>();

참고로 생성하는 제네릭 타입은 생략이 가능하다. 과거 코드와의 하위 호환 문제로 <>를 지정하지 않는 raw type을 지원하기도 하지만, 제네릭 타입을 사용할 때는 반드시 <>를 사용해서 사용시점에 타입을 지정하는 것이 좋다.

GenericBox<Integer> integerBox = new GenericBox<>();

✔️ 제네릭 관련 용어 정리

  • 제네릭(Generic) : 일반적인, 범용적인이라는 뜻.
  • 제네릭 타입(Generic Type) : 클래스나 인터페이스 정의 시 타입 매개변수를 사용하는 것, 제네릭 클래스와 제네릭 인터페이스를 모두 통틀어 제네릭 타입이라고 함.
  • 타입 매개변수(Type Parameter) : 제네릭 타입이나 메서드에서 사용되는 변수. GenericBox<T> 에서 T를 의미함.
  • 타입 인자(Type Argument) : 제네릭 타입을 사용할 때 제공되는 실제 타입. GenericBox<Integer>에서 Integer를 의미함.

Lv.3 계산기 구현 진행

enum 클래스에 연산자 담기

LV.3 계산기 구현을 시작했는데, enum 타입으로 연산자 타입에 대한 정보를 관리해야한다는 요구사항을 보고 고민에 빠졌다.

처음에는 이런 식으로 해야하는 건가? 했는데, 이미 Lv.2 계산기를 할 때 switch문에서 잘못된 연산자를 입력 받지 못하게 했기 때문에 enum을 왜 써야하는지 의문이었다. 사칙연산을 그대로 enum에 집어넣을 수 있다면 몰라도...?

public enum OperatorType {
    ADD("+"),
    SUBTRACT("-"),
    MULTIPLY("*"),
    DIVIDE("/")
}

알고보니 정말 집어넣을 수 있었다. enum 상수마다 동작을 다르게 정의해야 할 때 익명 클래스를 사용해 각 상수별로 고유한 메서드 구현을 할 수 있다.

public enum Operation {
    ADD {
        @Override
        public double operate(double x, double y) {
            return x + y;
        }
    },
    SUBTRACT {
        @Override
        public double operate(double x, double y) {
            return x - y; 
        }
    },
    MULTIPLY {
        @Override
        public double operate(double x, double y) {
            return x * y; 
        }
    },
    DIVIDE {
        @Override
        public double operate(double x, double y) {
            if (y == 0) {
                throw new ArithmeticException("0으로 나눌 수 없습니다.");
            }
            return x / y; 
        }
    };

    // 추상 메서드 선언
    public abstract double operate(double x, double y);
}

혹은 다음과 같이 람다식을 사용해 더 간결하게 작성할 수도 있다. 간단한 로직이므로 람다식으로 표현하는 것이 더 적합해보인다. 람다식만 적었다고 되는 것이 아니라, 람다식을 받는 생성자와 필드를 정의해줘야 한다.

public enum OperatorType {
    ADD((a, b) -> a + b),
    SUBTRACT((a, b) -> a - b),
    MULTIPLY((a, b) -> a * b),
    DIVIDE((a, b) -> {
        if (b == 0) throw new ArithmeticException("0으로 나눌 수 없습니다.");
        return a / b;
    });
    
    private final BiFunction<Double, Double, Double> operation;
    
    OperatorType(BiFunction<Double, Double, Double> operation) {
        this.operation = operation;
    }
    
    public double apply(double a, double b) {
        return operation.apply(a, b);
    }
}

여기서 BiFunction은 두 개의 입력값(T, U)을 받아서 하나의 출력값(R)을 반환하는 함수형 인터페이스이다. OperatorType 열거형은 각 연산(람다식)을 생성자에 전달받아 operation 필드에 저장한다. apply() 메서드는 저장된 operation 필드(람다식)를 호출하여 실제 연산을 수행하는 역할을 한다.

operation.apply(a, b)에서 볼 수 있듯이 BiFunction 인터페이스에 apply() 메서드가 있다. 찾아보니 다음과 같이 먼저 BiFunction 객체에 특정 로직을 수행하는 람다 표현식을 할당한 후, apply() 메서드로 두 개의 매개변수를 전달받아 특정 작업을 수행 후 값을 반환하게 된다.

BiFunction<Integer, Integer, String> biFunctionAdd =
          (num1, num2) ->  Integer.toString(num1 + num2);
          
System.out.println("100 + 50 = " + biFunctionAdd.apply(100, 50));

📝오늘의 회고

오늘은 Lv.3 계산기를 시작만 해서 내일 잘 완성할 수 있을지 모르겠다. 하지만 Lv.1, Lv.2 계산기는 너무 자연스럽게 만들어진 느낌이었다면, Lv.3는 좀 더 생각을 많이 하고 검색도 많이 해본 후에 시작해서 말 그대로 도전 과제의 느낌이었다. 개인적으로는 이렇게 적당히 도전적인 과제가 더 재밌고 공부할 것도 많아 시간이 더 잘 가는 것 같다. 내일은 과제를 모두 끝내고, 미뤘던 자바 강의 정리에 집중해야겠다.

📌내일의 할 일

  • 자바 문법 종합반 5주차 완강
  • 자바 중급2 섹션3 정리
  • Lv.3 계산기 구현 완료
  • 트러블슈팅 정리, 리드미 작성

0개의 댓글

관련 채용 정보