<추가 공부 내용>
스트림
스트림은 세가지 단계로 나뉨
1. 생성하기: 스트림 인스턴스 생성
2. 가공하기: 필터링, 맵핑 등 가공하는 중간 작업(ex: filtering, map)
3. 결과 만들기: 최종적(ex: forEach, collect)
- forEach: 기본 forEach문과 같은 의미로 실행된다고 볼 수 있다. 각각의 데이터를 뽑아서 보여준다.
//예시
studentList.stream().forEach(student -> System.out.println(student.getName()));
- map(): 데이터를 특정 데이터로 변환하는 역할. 데이터에 어떤 처리를 해서 보일 수 있다.
List<String> nameList = studentList.stream()
.map(student -> student.getName()).collect(Collectors.toList());
- filter(): 필요없는 데이터나 웹 요청을 걸러낼 때 사용. 즉 데이터의 전체를 필요로 하지 않은 경우 filter로 걸러낸다.
public List<Double> moreThanInput(Double input) {
return getResultList().stream().filter(num -> num > input).collect(Collectors.toList());
}
public List<Double> updateDoubleList(double sortingNum) {
return getResultList().stream().map(num -> num == sortingNum ? num * 2 : num).collect(Collectors.toList());
}
- collect(): 스트림의 최종 연산으로, 매개변수로 Collector(인터페이스) or Collectors(클래스, 메소드)가 필요. 스트림의 컬렉션/배열 변환시 많이 사용된다. (ex: toList(), toSet(), toMap(), toCollection, toArray())
list나 set이 아닌 특정 컬렉션을 지정하려면 Collectors.toCollection()에 해당 컬렉션의 생성자 참조를 매개변수로 넣어주면 된다.
//ex
List<String> names = studentStream.map(Student::getName)
.collect(Collectors.toList());
ArrayList<String> list = names.stream()
.collect(Collectors.toCollection(ArrayList::new));
<프로젝트 구현 3-2, 3-3>
제네릭 쓰기
제네릭을 써서 calculate 코드 수정하고
숫자 타입만을 받을 것이기 때문에 위에 클래스를 Number를 상속받는 제네릭 T 지정
결과값은 double로 나올 거니까 Number클래스의 메소드인 doubleValue()를 통해 double로 바꾼다
ArithmeticCalculator.java
OperatorType.java
package com.example.calculator3;
import java.util.function.BiFunction;
public enum OperatorType {
PLUS('+', (a, b) -> a.doubleValue() + b.doubleValue()),
MINUS('-', (a, b) -> a.doubleValue() - b.doubleValue()),
MULTIPLY('x', (a, b) -> a.doubleValue() * b.doubleValue()),
DIVIDE('%', (a, b) -> {
if (b.doubleValue() == 0) {
throw new ArithmeticException("0으로 나눌 수 없습니다");
}
return (double) (a.doubleValue() / b.doubleValue());
});
//PLUS 안의 +가 char operator라는 것을 지정
private final char operator;
//2개의 인자를 받고 1개의 객체를 리턴하는 함수형 인터페이스. 제네릭 사용을 위해 Number로 입력값 변경
private final BiFunction<Number, Number, Double> expression;
//생성자
OperatorType(char operator, BiFunction<Number, Number, Double> expression) {
this.operator = operator;
this.expression = expression;
}
//BiFunction 연산 결과 반환
public double calculate(Number a, Number b) {
return expression.apply(a, b);
}
//enum의 연산자(PLUS->+, MINUS->-) 꺼내기
public char getOperator() {
return operator;
}
}
입력받은 값을 전부 double로 만들어서 result가 double로 나올 수 있도록 하고 long타입이 아닌 Number를 입력받도록 수정
궁금한 점: 아무 타입의 숫자를 입력 받을 거면 그냥 Number를 쓰면 되는데 제네릭을 사용하는거랑 차이가 뭔지? ArithmeticCalculator에는 제네릭, OperatorType에는 Number로 타입을 바꿨는데 둘다 제네릭을 쓰는게 나은지, 둘다 Number를 써도 되는지
App.java에서 scanner를 통해 값을 입력받는데 이건 어떻게 수정하는가? scanner를 통해 long 변수를 입력받았었는데 double로도 입력받을 수 있도록 하는 것에 대해 고민을 많이 했다.
생각해본 결과 string으로 값을 받아서 "."이 있으면 double, 없으면 long으로 형변환을 시켜주도록 했다. (근데 이 방법으로 할 바에는 그냥 값을 double로 받으면 간단히 해결 될 일이 아닌가.. 과제 조건에서 단순히 타입을 double로 바꾸는게 아니라고 해서 이렇게 했는데 맞는 방법인진 모르겠다)
수정후 코드
parseNumber.java
App.java
메인 코드가 길어도 너무 길어서 메소드나 클래스로 따로 빼서 그걸 활용하는 식으로 변경하고 싶었다.
그리고 데이터 삭제 질문, 리스트 초기화 질문 등등 줄줄이 이어지는 질문이 너무 많아서 질문들을 옵션을 통해 선택하고 선택한 거에 따라서 결과가 나타나도록 수정
수정 전 코드
App.java
수정후 코드
Options.java
package com.example.calculator3;
import java.util.ArrayList;
import java.util.Scanner;
class Options {
public static boolean selectOptions(ArithmeticCalculator<?> calculator, Scanner sc) {
System.out.println("옵션을 선택하세요. 연산을 계속하려면 옵션 외 다른 키를 입력하세요:");
System.out.println("1: 가장 먼저 저장된 데이터 삭제");
System.out.println("2: 리스트 초기화");
System.out.println("3: 특정 값보다 큰 데이터 목록 보기");
System.out.println("exit: 프로그램 종료");
System.out.print("입력: ");
String input = sc.next();
switch (input) {
case "1": //데이터 삭제
if (!calculator.getResultList().isEmpty()) {
calculator.removeResult();
System.out.println("가장 먼저 저장된 데이터를 삭제했습니다.");
} else {
System.out.println("저장된 데이터가 없습니다.");
}
System.out.println("현재 데이터 목록: " + calculator.getResultList());
break;
case "2": //컬렉션 초기화(setter 활용)
calculator.setResultList(new ArrayList<>());
System.out.println("리스트를 초기화했습니다.");
System.out.println("현재 데이터 목록: " + calculator.getResultList());
break;
case "3": //입력받은 값보다 큰 결과값 출력(3-3)
System.out.print("기준값을 입력하세요: ");
double num;
try {
num = sc.nextDouble();
} catch (Exception e) {
System.out.println("유효하지 않은 값입니다.");
sc.nextLine();
break;
}
System.out.println("결과 목록에서 " + num + "보다 큰 값: " +
calculator.moreThanInput(num));
break;
case "exit": //반복 종료 묻기
System.out.println("프로그램을 종료합니다.");
return true;
default: //연산 반복
System.out.println("연산을 시작합니다");
}
return false;
}
}
App.java
package com.example.calculator3;
import java.util.ArrayList;
import java.util.Scanner;
public class App {
public static void main(String[] args) {
ArithmeticCalculator<Number> calculator = new ArithmeticCalculator<>();
while (true) {
Scanner sc = new Scanner(System.in);
System.out.print("첫 번째 숫자를 입력하세요: ");
Number first;
try {
first = DataType.parseNumber(sc.next());
} catch (IllegalArgumentException e) {
System.out.println("오류: " + e.getMessage());
continue;
}
System.out.print("두 번째 숫자를 입력하세요: ");
Number second;
try {
second = DataType.parseNumber(sc.next());
} catch (IllegalArgumentException e) {
System.out.println("오류: " + e.getMessage());
continue;
}
//사칙 연산 기호 입력받기
System.out.print("사칙연산 기호를 입력하세요(ex:+,-,x,%): ");
try {
char operator = sc.next().charAt(0);
double result = calculator.calculate(first, second, operator);
System.out.println("결과: " + result);
} catch (Exception e) {
System.out.println("오류: " + e.getMessage());
continue;
}
//컬렉션 가져오기(getter 활용)
System.out.println("저장된 데이터: " + calculator.getResultList());
//메인코드가 너무 길어져서 클래스로 작성
if (Options.selectOptions(calculator, sc)) break;
}
}
}
'프로젝트 > 계산기' 카테고리의 다른 글
[계산기 프로젝트] 트러블 슈팅-예외처리/프로젝트 느낀점 (1) | 2025.01.08 |
---|---|
[계산기 프로젝트] enum, 람다, BiFunction (0) | 2025.01.07 |