console-calculator/src/main/java/org/example/lv3 at main · BibliyaSeo/console-calculator
Enum 사용
<aside> 📌
처음엔 Enum에서 추상메서드를 이용해서 계산을 바로 하려고 했다. 그러면 switch-case문을 이용하지 않아도 되어서 편하다고 생각했는데! int로 받을 때는 문제가 없었는데 제네릭을 이용해야 하기 때문에 Number를 사용해야 해서 계산할 때 모든 값에 .doubleValue();
를 적어줘야 하는게 코드상 예뻐보이지 않고 새로운 연산을 추가할 수도 있으니 따로 분리해 두는 게 더 괜찮을 것이라 생각했다.
</aside>
기존 코드
public enum OperatorType {
ADD('+') {
@Override
public double calculate(Number a, Number b) {
return a.doubleValue() + b.doubleValue();
}
},
SUB('-') {
@Override
public double calculate(Number a, Number b) {
return a.doubleValue() - b.doubleValue();
}
},
MUL('*') {
@Override
public double calculate(Number a, Number b) {
return a.doubleValue() * b.doubleValue();
}
},
DIV('/') {
@Override
public double calculate(Number a, Number b) {
return a.doubleValue() / b.doubleValue();
}
};
// 생략
public abstract double calculate(Number a, Number b);
// 생략
}
바꾼 코드
public enum OperatorType {
ADD('+'),
SUB('-'),
MUL('*'),
DIV('/');
// 생략
// calculate 삭제
// 생략
}
추가된 코드
public class CalculatorUtil {
public static double calculate(Number a, Number b, OperatorType operator) {
double num1 = a.doubleValue();
double num2 = b.doubleValue();
return switch (operator) {
case ADD -> num1 + num2;
case SUB -> num1 - num2;
case MUL -> num1 * num2;
case DIV -> {
if (num2 == 0.0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
yield num1 / num2;
}
default -> throw new IllegalArgumentException("지원하지 않는 연산자입니다.");
};
}
}
<aside> 📌
일반 switch-case문
을 작성했다가 노란색 줄이 뜨길래 확인해 봤더니 Java 14부터 도입된 enhanced switch expression
으로 간결하게 표시할 수 있다고 해서 변경해 봤다.
</aside>
Generic 사용
// ArithmenticCalculator.java
ArithmeticCalculator<T extends Number>
→ Integer
, Double
, Float
등이 들어올 수 있도록 설정했다
// Main.java
calculateAdd(T num1, T num2, OperatorType operator)
→ 클래스 제네릭 타입 T
를 사용해서 호출자가 지정한 타입과 맞춰서 입력받을 수 있게 설정했다
// ArithmeticCalculator.java
CalculatorUtil.calculate(num1, num2, operator)
// CalculatorUtil.java
public static double calculate(Number a, Number b, OperatorType operator)
→ Number로 받을 수 있게 설정하였다
왜 Number로 받아? 여긴 왜 T로 안 받아?
일단!
<aside> 📌
CalculatorUtil.calculate(T a, T b, OperatorType operator)
처럼 메서드에 제네릭 타입 파라미터 T를 직접 선언하지 않은 상태에서 클래스나 메서드 레벨 제네릭 T를 사용하는 것은 불가능하다고 한다.
CalculatorUtil
클래스나 calculate
메서드가 제네릭 타입 T를 선언하지 않았다면 사용할 수 없다!!
</aside>
public class CalculatorUtil {
public static double calculate(T a, T b, OperatorType operator) { ... } // ❌ 오류
}
→ 위 코드는 컴파일 에러다. 왜?? T
가 무엇인지 모르기 때문이다..!
CalculatorUtil
를 제네릭 메서드로 선언해야 한다면??public class CalculatorUtil {
public static <T extends Number> double calculate(T a, T b, OperatorType operator) {
// 생략
double num1 = a.doubleValue();
double num2 = b.doubleValue();
// 생략
}
}
→ <T extends Number>
를 저렇게 앞에 선언해야 calculate
메서드는 제네릭 메서드가 되고, 호출할 때 타입 추론이 가능해져서 명시적으로 타입을 줄 수 있다고 한다.
<aside> 📌
나는 1번으로 사용했다!!!
왜?
내부에서 a.doubleValue()
처럼 Number
메서드만 사용할 거라 굳이 제네릭으로 받을 필요 없이 그냥 Number
타입으로 받았다. 어차피 다 더블이야~
</aside>
내부 int에서 double로 변경
<aside> 📌
lv1과 lv2에서는 양의 정수만 사용했기 때문에 int
를 사용했는데 lv3에서는 실수를 받아야 해서 double
로 변경해 주고 음수를 거르는 if
문 조건은 삭제해 주었다.
</aside>
double과 Double의 차이
<aside> 📌
double은 null값을 받을 수 없는 기본 자료형이고 Double은 레퍼 클래스이다. 제네릭/컬렉션에서는 double을 사용하지 못하고 Double만 사용 가능하다.
</aside>
나눗셈에서 undefined를 설정했는데 double로 바꾸고 나서는 Infinity가 나오는 이유와 Infinity가 아니라 undefined가 나오게 설정하는 법
<aside> 📌
int
로 했을 때는 undefined
로 잘 내려왔는데 double
로 바꾸고 나니 Infinity
가 나온다. 알고 보니 double
타입에서는 0으로 나누는 게 Infinity
나 NaN
로 반환하고 에러로 떨어지지 않는 것이었다. 그럼 이 상태에서 undefined
로 result를 보여주고 싶다면 어떻게 해야할까? 방법은 throw new ArithmeticException
를 날려서 try-catch
로 잡아주는 것이었다.
</aside>
case DIV -> {
if (num2 == 0.0) {
throw new ArithmeticException("0으로 나눌 수 없습니다.");
}
yield num1 / num2;
}
try {
double calcResult = CalculatorUtil.calculate(num1, num2, operator);
result = String.valueOf(calcResult);
} catch (ArithmeticException e) {
// System.out.println(e.getMessage()); 0으로 나눌 수 없습니다.
result = "undefined";
} catch (IllegalArgumentException e) {
result = "잘못된 연산입니다.";
}
Enum에서 연산자 찾는 방법
<aside> 📌
Enum을 썼는데 내가 가져오는 +,-,*,/가 ADD인지 DIV인지 어떻게 알지?라는 의문이 생겼다. 향상된 for문을 돌려서 입력한 값이 OperatorType Enum 중에 어떤 거랑 매칭되는지 찾아야했다. 내가 +를 입력하면 ADD를 반환하는데 +와 ADD가 매핑되어 있기 때문!!
</aside>
public static OperatorType matchOperator(char operator) {
for (OperatorType op : values()) {
if (op.getOperator() == operator) {
return op;
}
}
throw new IllegalArgumentException("지원하지 않는 연산자입니다: " + operator);
}