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);
}