자바 미니 프로젝트 - 계산기 만들기 Part2

Zyoon·2025년 4월 22일

미니프로젝트

목록 보기
5/35
post-thumbnail

💡자바 콘솔로 간단한 사칙연산 계산기 만들기2 - 추가 기능


추가 사항

  • 기존 계산기에서 일부 기능 추가 및 타입 변경
  • 객체 필드를 Generic 타입으로 변경하여 같은 변수로 int, double 타입 모두 같은 변수로 관리
  • enum 으로 연산 관리
  • 특정 결과 값만 조회 후 리스트 생성

코드 정리


필드부분 - 제네릭 타입으로 수정

//제네릭 클래스 생성(Getter, Setter)
public class ArithmeticCalculatorData<T extends Number> {
  private T firstNum;
  private T secondNum;
  private char operator;
  private T resultNum; 
  • 필드값은 숫자만 받을 수 있도록 Number 클래스 상속 → 숫자 타입 (Integer, Double, Float, Long, Short, Byte) 만 가능

숫자 입력 메서드

//숫자 입력 부분
ArithmeticCalculatorData calData = new ArithmeticCalculatorData<>();
double tempNum;
while (true) {
    System.out.println("\n첫번째 숫자를 입력하세요");
    System.out.print("숫자 입력 : ");
    try {
        tempNum = sc.nextDouble();
        if (isInt(tempNum)) {
            calData.setFirstNum((int) tempNum);
        } else {
            calData.setFirstNum(tempNum);
        }
        break;
    } catch (Exception e) {
        System.out.println("숫자를 제대로 입력해 주세요");
        sc.nextLine();
    }
}
// int,double 판독 메서드
public static boolean isInt(double num) {
		// int 값의 범위를 벗어나거나
    if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) {
        return false;
    } else return num % 1 == 0.0; // 정수인지 판독 (실수일경우 소수점이 남음)
}
  • 제네릭 클래스로 수정 : int, double 모두 받을 수 있음
  • nextIntnextDouble 로 수정
  • 입력받은 double 값이 int 로 변환 가능한지 확인하는 메서드 추가
  • 입력 값이 int 의 범위를 벗어나면서(조건1), 정수일 경우(조건2) int로 판독
  • 변환후 저장이유 : 연산결과 출력 시 {[연산 결과{ 5.0 + 6.0 = 11.0 }]} 이런식으로 불필요한 소수점 나오는 것 방지

Enum 으로 연산 관리 추가

// enum 으로 연산 관리
public enum OperatorType {
    PLUS('+', (x, y) -> x + y),
    MINUS('-', (x, y) -> x - y),
    MULTIPLY('*', (x, y) -> x * y),
    DIVIDE('/', (x, y)-> x / y);

    // 필드
    private final char symbol; // +,-,*,/
    private final BiFunction<Double, Double, Double> operate;

    // 생성자
    OperatorType(char symbol, BiFunction<Double, Double, Double> operate) {
        this.symbol = symbol;
        this.operate = operate;
    }

    // 연산 값 리턴
    public double operate(double x, double y) {
        return operate.apply(x, y);
    }

		// Symbol 값 리턴 (+,-,*,/)
    public char getSymbol() {
        return symbol;
    }
}
  • enum 으로 연산을 모두 관리
  • 각각의 연산을 정의 후 람다식으로 표현
  • 함수형 인터페이스 BiFunction 필드에 정의
     //파라미터가 2개일 경우
     BiFunction<파라미터 1, 파라미터 2, 리턴 값>
     
     //파라미터가 1개일 경우
     Function<파라미터 1, 리턴 값>
  • BiFunction 타입의 operate 호출 시 apply() 함수로 미리 정의해둔 람다식 함수 실행

연산 메서드

public ArithmeticCalculatorData calculate(ArithmeticCalculatorData calData) {
		
	//Number 값으로 담겨있는 값들을 double 타입으로 변환
    double resultNum = 0.0;
    double firstNum = calData.getFirstNum().doubleValue();
    double secondNum = calData.getSecondNum().doubleValue();
    char operatorSymbol = calData.getOperator();

	//enum 으로 정의 된 operate 함수 사용하여 연산
    if(operatorSymbol == OperatorType.PLUS.getSymbol()){
        resultNum = OperatorType.PLUS.operate(firstNum, secondNum);
    }else if(operatorSymbol == OperatorType.MINUS.getSymbol()){
        resultNum = OperatorType.MINUS.operate(firstNum, secondNum);
    }else if(operatorSymbol == OperatorType.MULTIPLY.getSymbol()){
        resultNum = OperatorType.MULTIPLY.operate(firstNum, secondNum);
    }else if(operatorSymbol == OperatorType.DIVIDE.getSymbol()){
        resultNum = OperatorType.DIVIDE.operate(firstNum, secondNum);
    }

	//결과값은 int,Double 판독하여 객체로 담는다.
    if (isInt(resultNum)) calData.setResultNum((int)resultNum);
    else calData.setResultNum(resultNum);

    return calData;
}
  • getSymbol() 로 비교 하기 위해서 기존 case 문에서 if-else 문으로 변경
  • 기존의 연산과정을 enum에서 수행하고 해당 함수만 호출하는 형식으로 변경
  • 객체에 Number 타입으로 담겨있기 때문에 doubleValue()double 타입으로 변경 후 연산
  • 연산 결과는 isInt() 메서드로 double,int 판단 후 해당 값으로 Set

출력 결과

첫번째 숫자를 입력하세요
숫자 입력 : 64.565

연산자를 입력하세요
연산자 입력 : -

두번째 숫자를 입력하세요
숫자 입력 : 44

연산 결과 : 20.565
전체 결과{[연산 결과{ 64.565 - 44 = 20.565 }]}
  • int 타입은 정수로 double 타입은 소수점 추가하여 출력

연산 결과 중 특정 결과 값만 조회 기능 추가

//Scanner 입력 값 보다 결과 값이 높은 결과만 조회하는 메서드
public void checkUpperResult(ArithmeticCalculator cal, Scanner sc){
		if (cal.getResultList().isEmpty()) {
            System.out.println("연산 데이터가 존재하지 않습니다.");
            return;
    }
    while(true){
        try {
            System.out.println("숫자를 입력해주세요. 입력값보다 높은 결과값이 조회됩니다.");

            double inputNum = sc.nextDouble();
            sc.nextLine();

            //필터활용하여 새로운 데이터 조회
            List<ArithmeticCalculatorData> list = cal.getResultList().stream()
                    .filter(result -> result.getResultNum().doubleValue() > inputNum)
                    .collect(Collectors.toList());

            //list 출력
            System.out.println("\n"+inputNum+"보다 높은 결과 : "+list);          
            return;

        } catch (Exception e) {
            System.out.println("똑바로 입력해 주세요.");
            sc.nextLine();
        }
    }
}
  • 단순 결과 조회기
  • nextDouble() 로 입력 값 생성 (문자, 기호 입력 시 에러)
  • Stream 기능 사용하여 조회 할 리스트 생성 후 결과 값 출력
  • filter() 부분의 inputNum 의 비교 연산자 변경으로 결과 값 조절 가능

이슈 사항


1. Number 타입의 연산

문제점

  • Number 타입의 변수로 직접적인 연산 시도했으나 오류 발생
    Number num1 = 2;
    Number num2 = 3;
    Number result = num1 + num2 // 오류 발생

원인

  • 자바에서는 int, double 같은 기본형은 연산자로 연산이 가능하다
  • 하지만 Number 객체에는 적용이 불가능

해결

  • 기본형으로 변환 후 연산한 뒤에 기본형 변수에 담아준 뒤에 연산을 진행한다.
    Number num1 = 5;
    Number num2 = 3;
    //int 타입
    int resultInt = num1.intValue() + num2.intValue();
    //double 타입
    double resultDouble = num1.doubleValue() + num2.doubleValue();
  • 추가로 Integer, Double 같은 래퍼클래스도 연산은 가능하다. 하지만 기본형처럼 직접적인 연산이 아니 자바의 오토언박싱 기능으로 자동 박싱/언박싱 해주는 것이다.

2. double 타입 출력 시 비정상

문제점

  • double 타입끼리 연산 시 소수점 자리가 비정상 출력되어 나오는 현상
    **연산 결과{ 54.55 + 22.4 = 76.94999999999999 }**

원인

  • 결론부터 말하자면, 출력 문제이지 값의 문제가 아니다.
  • 자바에서 double 타입은 2진수 기반 부동소수점 방식으로 값을 저장하는데, 10진수 소수를 2진수로 완벽하게 표현할 수 없는 경우가 많다.
  • 그래서 그 근사값이 저장되어 있는데 출력을 하게되면 비정상으로 보이게 된다.
  • 내부적으로는 double 값 그대로 저장되어 있는 것이다.

해결

  • 내부적으로는 제대로 저장되어 있기때문에 내부적인 값 까지 바꿀 필요는 없었다.
  • 출력문만 정상적으로 보이게 하기 위해 DecimalFormat() 메서드 사용
    System.out.println(
    new DecimalFormat("#.############").
    format(calData.getResultNum()));
    
    //출력 결과 : 54.55 + 22.4 = 76.95 
  • 이렇게 하면 내부적인 double 값은 바꾸지 않고 출력문 형식만 변경해서 보여줄 수 있다.
  • 만약, 출력문 뿐만이 아니라 내부 값까지 변경해야 할 경우, Math.round 로 특정 위치까지 반올림하여 처리 할 수도 있다.

회고


  • 연산결과를 Stack 으로 관리하였으나 특정 결과를 조회하는 기능 추가하면서 조회 과정이 더 용이할 수 있도록 List 로 변경하였다. 덕분에 Stream으로 인덱스 조회하기가 더 수월해졌다.
  • 객체 클래스를 제네릭 클래스로 변경하였는데, Enum 클래스에서는 제네릭 클래스로 적용이 안되기 때문에 해당 부분을 어떻게 처리하는지 어느 부분에서 형변환 처리할지를 많이 고민했다. 이 부분은 객체 Getter 부분에서 double 타입으로 변경하여 처리하였다.
  • Scanner 로 값을 받을 때 int와 double 타입을 어떻게 처리할까 하다가 int와 double 을 판단해주는 메서드를 따로 생성해서 관리하도록 했다.
  • 기능 추가 및 수정하는 부분에서 기존 코드 변경하는게 좀 힘들었다. 간단한 코드라도 유지보수에 더 유리할 수 있도록 고민해보는 방향으로 작업해야 할 것 같다.
  • 할 수 있다면 간단한 GUI로도 작업해보는 것도 좋을 것 같다.

📗미니 프로젝트 - 계산기 만들기 Part1

📓계산기 만들기 Github

profile
기어 올라가는 개발

0개의 댓글