문제 발생 배경
int num1 = 0;
// 문자열을 정수로 변환
num1 = Integer.parseInt(firstInput);
위의 양의 정수만 입력받는 시스템에서 다른 타입도 입력받을 수 있도록 변경해야 했다.
기존의 시스템은 문자열 `firstInput`을 특정 타입으로 파싱한 값을 변수에 저장하고, 변수를 연산 클래스인 Calculator에 전달했다.
정수와 실수 모두 입력 가능한 프로그램을 만들기 위해,
문자열을 정수로 파싱하는 코드와 실수로 파싱하는 코드를 분리해서
최종적으로는 정수-정수, 정수-실수, 실수-실수의 연산이 가능해지도록 아래와 같이 코드를 수정했다.
double doubleNum;
int intNum;
// 입력한 숫자의 타입을 실수로 변환
if (firstInput.contains(".")) {
doubleNum = Double.parseDouble(firstInput);
calculator.setNum1(doubleNum);
} else {
// 입력한 숫자의 타입을 정수로 변환
intNum = Integer.parseInt(firstInput);
calculator.setNum1(intNum);
}
이 코드는 문자열에 .이 포함되어 있으면 실수로 판단해서 double 타입으로 파싱하고,
.이 포함되지 않은 경우는 int 타입으로 파싱하는 코드이다.
문제 1
public class Calculator<T extends Number> {
...
public void setNum1(T num1){
this.num1 = num1;
}
public void setNum2(T num2){
this.num2 = num2;
}
public double calculate(){
switch (calc) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
result = (double) num1 / num2;
break;
case '%':
result = num1 % num2;
break;
default:
System.out.println("잘못된 연산 기호입니다.");
break;
}
resultList.add(result);
return result;
}
...
}
위의 switch 문에서 제네릭을 이용해서 두 가지 타입의 수를 전달하는 것 까진 성공적이었으나, 다른 타입끼리는 연산이 불가능해서
내부의 연산식 코드에 모두 에러가 발생했다.
문제 1 해결
public void setNum1(T num1){
this.num1 = num1.doubleValue();
}
public void setNum2(T num2){
this.num2 = num2.doubleValue();
}
피연산자의 타입이 일치해야 해서 상속받은 Number의 메서드인 doubleValue()를 통해 두 피연산자의 타입을 double로 일치시켰고,
제대로 연산이 수행되는 걸 볼 수 있었다.
문제 2
스트림, 람다를 프로그램에 적용시키기 위해 아래의 코드를 추가했다.
사용자에게 숫자를 하나 입력받고, 입력받은 수를 저장한 변수 n과 `calculator.getResult()`를 통해 받아온 연산 결과 리스트의 각 데이터를 비교해서 n보다 큰 데이터만 출력하는 코드이다.
double n = scanner.nextInt();
System.out.println(
calculator.getResult().stream()
.filter(num -> num > n)
.collect(Collectors.toList())
);
그런데 스트림으로 가져온 리스트의 각 데이터 num이 Object로 전달돼서 연산자로 비교가 안되는 오류가 발생했다.
// Calculator.java
private List<Double> resultList = new ArrayList<>();
public List<Double> getResult(){
return resultList;
}
리스트 내부 요소의 타입은 Double로 지정되어 있었고 getResult()의 반환타입도 List<Double>로 잘 지정되어 있어서
스트림을 통해 각 데이터를 추출했을 때는 Double이 전달되어야 했다.
문제 2 해결
// Calculator.java
public class Calculator<T extends Number> {..}
// App.java
// 기존 코드
Calculator calculator = new Calculator();
// 수정한 코드
Calculator<Double> calculator = new Calculator<>();
알고보니 Calculator 객체를 생성할 때 타입 매개변수를 지정해주지 않아서 생긴 오류였다.
`calculator.getResult()`의 반환 타입이 제네릭과 관계없는 List<Double>이었지만,
Java에서는 객체 생성할 때 타입 매개변수를 지정해주지 않으면 안전성을 위해 Object로 취급해서 전달하는 것이다.
문제 3
intNum = Integer.parseInt(firstInput);
calculator.setNum1(intNum);
객체 생성 코드를 Calculator<Double>로 수정하면서
최상단 '문제 발생 배경'에 첨부한 코드인, 입력값을 정수형으로 파싱하는 부분에서 오류가 발생했다.
Calculator 클래스에 전달되는 객체가 실수형 객체로 지정되었는데, 정수형을 전달하려고 해서 발생한 오류였다.
문제 3 해결
public void setNum1(Number num1){
this.num1 = num1.doubleValue();
}
public void setNum2(Number num2){
this.num2 = num2.doubleValue();
}
문제 1에서 수정한 코드를 보면 매개변수의 타입이 T여서,
객체를 생성할 때 지정한 Double이 아니면 값의 전달이 불가능하기 때문이다.
정수도 해당 클래스에 전달할 수 있도록 메서드의 매개변수 타입을 T가 아닌 Number로 변경했다.
Integer, Double 모두 Number 클래스를 상속하는 클래스이기 때문에 정수와 실수 모두 전달이 가능하다.
결과물
오류가 연달아 발생하면서 시간이 많이 걸렸지만, 잘 해결한 덕분에 이렇게 정수와 실수의 연산도 가능한 계산기를 만들 수 있었다.
기능을 추가하면서 Integer와 Double 클래스가 Number 클래스를 상속받고 있다는 사실을 처음 알게 되었는데, Number 클래스를 이렇게 활용할 수 있었다는 것도 배울 수 있어서 유의미한 트러블 슈팅이었다.
기초적인 계산기 프로그램을 구현하면서 발생한 트러블 슈팅
👇 👇 👇
2025.02.28 - [트러블 슈팅] - [Java] 계산기 프로그램의 출력값 오류 이슈
[Java] 계산기 프로그램의 출력값 오류 이슈
문제 상황package com.example.calculator;import java.util.Scanner;public class Calculator { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int num1 = scanner.nextInt(); int num2 = scanner.nextInt(); char calc = scanne
go-getter1kim.tistory.com
'트러블 슈팅' 카테고리의 다른 글
[Java] 리스트에 담긴 데이터 중복 출력 이슈 (1) | 2025.03.12 |
---|---|
[Java] BufferedWriter 출력 안 됨 이슈 (1) | 2025.03.11 |
[Java] 계산기 프로그램의 출력값 오류 이슈 (0) | 2025.02.28 |
[Java] for-each문으로 배열 반복 시 이슈 (0) | 2025.02.24 |
[Java] for-each문에 2차원 배열 사용 이슈 (0) | 2025.02.22 |