문제 발생 배경

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