[TIL] Java Enum과 Generic 활용하여 계산기 만들기 (25-01-06)
🤖 25-01-06 진행 사항 🤖
- ch2. 계산기 과제 Lv 3 : Enum, 제네릭, 람다&스트림을 이해한 계산기 만들기
- Enum
- Generic
- 예외처리
Enum
Enum 타입을 활용하여 연산자 타입에 대한 정보를 관리하고 이를 사칙연산 계산기 ArithmeticCalculator 클래스에 활용
Enum 개념 정리
Enum에 대한 개념을 먼저 정리하고 과제에 적용시켰다.
2025.01.06 - [Java Study/Advanced] - [Java] Enum 사용법과 활용 예시
[Java] Enum 사용법과 활용 예시
enum 클래스enum은 enumeration의 약자로 열거형이라고 한다. 서로 관련 있는 상수들의 집합을 정의할 때 사용된다. 기본 개념enum은 여러 상수를 그룹화하여 사용할 수 있는 데이터 타입.상수를 정의
mannakingdom.tistory.com
OperatorType Enum 클래스
Enum 상수를 ADD, SUBTRACT, MULTIPLY, DIVIDE로 정의하고,
추상 메서드를 calculate를 추가하여, 각 상수별로 연산 결과를 return하도록 했다.
package com.example.calculator3;
public enum OperatorType {
ADD {
@Override
public int calculate(int num1, int num2) {
return num1 + num2;
}
},
SUBTRACT {
@Override
public int calculate(int num1, int num2) {
return num1 - num2;
}
},
MULTIPLY {
@Override
public int calculate(int num1, int num2) {
return num1 * num2;
}
},
DIVIDE {
@Override
public int calculate(int num1, int num2) {
return num1 / num2;
}
};
public abstract int calculate(int num1, int num2);
}
ArithmeticCalculator 클래스의 calculate 함수에서도 각 상수의 메서드를 호출하는 형식으로 변경했다.
Generic
피연산자를 여러 타입으로 받을 수 있도록 기능을 확장
Generic 개념 정리
Generic에 대한 개념을 먼저 정리하고 과제에 적용시켰다.
2025.01.06 - [Java Study/Advanced] - [Java] Generic의 원리와 활용법
[Java] Generic의 원리와 활용법
GenericJava의 제네릭(Generic)은 데이터 타입을 일반화하여 코드 재사용성을 높이고 타입 안정성을 보장하는 기능이다.Generic은 일반적(generalized)이라는 뜻으로, 다양한 데이터 타입에서 동작하도록
mannakingdom.tistory.com
ArithmetiCalculator 클래스
int형 변수만 사용하던 클래스를 제네릭 타입으로 변경했다.
<T extends Number>로 Number 클래스 또는 그 하위 클래스들을 사용하도록 선언했다.
package com.example.calculator3;
import java.util.ArrayList;
import java.util.List;
public class ArithmeticCalculator <T extends Number> {
// 연산 결과를 저장하는 컬렉션 타입 필드를 가진 Calculator 클래스를 생성
private List<T> result = new ArrayList<>();
// 사칙연산을 수행한 후, 결과값을 반환하는 메서드 구현
// 매개변수 타입은 제네릭, 반환은 항상 double
public double calculate(T num1, T num2, char op) {
return switch (op) {
case '➕', '+' -> OperatorType.ADD.apply(num1, num2);
case '➖', '-' -> OperatorType.SUBTRACT.apply(num1, num2);
case '✖', '*' -> OperatorType.MULTIPLY.apply(num1, num2);
case '➗', '/' -> OperatorType.DIVIDE.apply(num1, num2);
default -> 0;
};
}
/* Getter 메서드 구현 */
public T getResult() {
return this.result.get(result.size() - 1); // 마지막 요소 리턴
}
/* Setter 메서드 구현 */
public void setResult(T answer) {
this.result.add(answer);
}
/* Calculator 클래스에 저장된 연산 결과들 중 가장 먼저 저장된 데이터를 삭제하는 기능을 가진 메서드를 구현한 후
App 클래스의 main 메서드에 삭제 메서드가 활용될 수 있도록 수정 */
public void removeResult() {
this.result.remove(0);
}
}
calculate 함수의 반환값은, 일반적인 산수 계산의 결과를 고려해 double로 지정했다.
+ OperatorType Enum 클래스의 연산 매개변수도 제네릭 타입으로 변경했다.
예외처리 추가
App.java에서 연산을 수행할 변수들을 입력 받을 때, 입력 받은 값에 대한 예외처리를 추가했다.
Enum 개념을 학습하며 알게 된, Enum 필드와 생성자, 메서드를 활용했다.
(아직 진행중!)
App.java
피연산자를 입력받을 때는, 길이가 2인 double형 배열에 입력받는다.
이 때, 문자열로 입력받은 값을 Double.parseDouble 메서드를 실행하고
NumberFormatException으로 입력받은 문자열이 숫자가 아니라면 재입력하도록 해줬다.
// 연산할 숫자 2개 입력 받기
// 입력 받은것이 숫자가 맞는지 확인
String userInput;
Number[] numbers = new Number[2];
for (int i=0; i<2; i++) {
System.out.print((i+1) + " 번째 숫자를 입력하세요: ");
userInput = sc.next().trim();
try {
numbers[i] = Double.parseDouble(userInput);
} catch (NumberFormatException e) {
System.out.println(userInput + "은 숫자가 아닙니다. 숫자를 다시 입력하세요.");
--i;
//continue;
}
}
사칙연산 기호를 입력받을 때는, Enum 상수의 필드값과 비교하는 isValidOperator 함수를 호출하여 판별했다.
추가로, 나누기 연산의 경우 isDivisionOperator 함수를 호출하여 확인했다.
// 사칙연산 기호(➕,➖,✖️,➗) 입력 받기
System.out.print("사칙연산 기호를 입력하세요: ");
try {
char operator = sc.next().trim().charAt(0);
if (OperatorType.isValidOperator(operator)) {
if (OperatorType.isDivisionOperator(operator) && numbers[1].equals(0)) {
System.out.println("나눗셈 연산에서 분모(두번째 정수)에 0이 입력될 수 없습니다.");
}
else {
calculator.setResult(calculator.calculate(numbers[0], numbers[1], operator));
System.out.println("결과: " + calculator.getResult());
calculator.removeResult();
}
}
else {
System.out.println("사칙연산 기호가 적절하지 않습니다.");
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
OperatorType Enum 클래스
1) 연산 메서드의 매개변수는 제네릭 <T extends Number> 적용하고, double값을 반환하는 메서드로 변경
2) operationSymbol('+','-','*','/)와 operationEmoji('➕','➖','✖️','➗') 필드를 추가하여 각 상수별로 값을 지정
3) App.java에서 호출하는 isValidOperator 메서드 생성 : valuse()로 필드 값들과 입력값을 비교
4) App.java에서 호출하는 isDivisionOperator 메서드 생성
package com.example.calculator3;
public enum OperatorType {
ADD('+', '➕') {
@Override
public <T extends Number> double apply(T num1, T num2) {
return num1.doubleValue() + num2.doubleValue();
}
},
SUBTRACT('-', '➖') {
@Override
public <T extends Number> double apply(T num1, T num2) {
return num1.doubleValue() - num2.doubleValue();
}
},
MULTIPLY('*', '✖') {
@Override
public <T extends Number> double apply(T num1, T num2) {
return num1.doubleValue() * num2.doubleValue();
}
},
DIVIDE('/', '➗') {
@Override
public <T extends Number> double apply(T num1, T num2) {
return num1.doubleValue() / num2.doubleValue();
}
};
// 필드
private final char operationSymbol;
private final char operationEmoji;
// 생성자
OperatorType(char symbol, char emoji) {
this.operationSymbol = symbol;
this.operationEmoji = emoji;
}
// 입력받은 사칙연산 기호가 올바른지 판별
public static boolean isValidOperator(char input) {
for (OperatorType op : OperatorType.values()) {
if (op.operationSymbol == input || op.operationEmoji == input) {
return true;
}
}
return false;
}
// 나눗셈 연산인지 판별
public static boolean isDivisionOperator(char input) {
if (OperatorType.DIVIDE.operationSymbol == input
|| OperatorType.DIVIDE.operationEmoji == input) {
return true;
}
return false;
}
// 매개변수는 제네릭 타입(Number 또는 그 하위 클래스들)으로 받고,
// 반환 타입을 항상 double 타입으로 고정
public abstract <T extends Number> double apply(T num1, T num2);
}