Information Security Study

240130 자바(getter/setter의 문제점, stream API, Optional) 본문

네트워크 캠퍼스/JAVA

240130 자바(getter/setter의 문제점, stream API, Optional)

gayeon_ 2024. 1. 30. 15:50

생성자 주입이 좋고 setter를 되도록이면 쓰지 말아야 하는 이유

기본 생성자(빈 객체를 만드는 경우) setter를 이용해서 추가적인 데이터 수정을 허용한다면

모든 필드를 초기화하지 않고 넘어가는 경우가 발생할 수 있다.

 

따라서 모든 필드를 초기화하도록 생성자 주입을 강요하는 것이 더 좋다.

 

불완전한 인스턴스의 생성을 억제할 수 있다는것이 바로 생성자 주입의 좋은 점이다.

 

그래서 외부와의 접점이 있는 클래스를 DTO로 먼저 사용하고

내부적으로는 따로 사용하는 Entity 클래스를 이용하는 것이 좋은 이유다.

 

 

stream API

Collection을 상속받은 구현체에서는 stream API를 사용할 수 있다.

 

.stream().다른메서드()

 

  • foreach

반복문은 foreach를 이용해 JS의 .map처럼 활용할 수 있다.

 

내부적으로 실행할 때는 람다식을 이용해야 한다.

 

다만 foreach에서는 i와 같은 인덱스번호가 없고, break문을 사용할 수 없다.

 

 

forIter 예제

package stream.foreach;

import java.util.ArrayList;
import java.util.List;

public class ForIterExampleMain {

	public static void main(String[] args) {
		List<Integer> integerList = new ArrayList<>();
	
		integerList.add(10);
		integerList.add(20);
		integerList.add(30);
		integerList.add(40);
		integerList.add(50);
		integerList.add(60);
		integerList.add(70);

		// integerList에서 40을 탐색하고 찾으면 즉시 종료시키는 반복문
		for(int i = 0; i < integerList.size(); i++) {
			System.out.println(integerList.get(i));
			if(integerList.get(i) == 40) {
				System.out.println("40 찾았다.");
				break;
			}
		}
	}

}

실행 결과

 

 

stream().foreach 예제

package stream.foreach;

import java.util.ArrayList;
import java.util.List;

public class ForEachIterExampleMain {

	public static void main(String[] args) {
		List<Integer> integerList = new ArrayList<>();
		
		integerList.add(10);
		integerList.add(20);
		integerList.add(30);
		integerList.add(40);
		integerList.add(50);
		integerList.add(60);
		integerList.add(70);
		
									// 람다식을 내부에서 받는 반복문 
		integerList.stream().forEach(integer -> { // integer변수에 매번 10, 20, 30... 순차대입
			System.out.println(integer);
			
			if(integer == 40) {
				System.out.println("40 찾았다.");
//				break; // 일반 for문이 아니므로 break의 대상이 아니다.
				throw new RuntimeException("40을 찾아서 멈춥니다."); // 권장되는 방법은 아니다.
			}
		});
	}
}

실행 결과

 

 

  • filter

대신 대안으로 filter를 사용할 수 있다.

return true는 조건을 만족하는 요소에

return false는 조건을 불만족하는 요소에 걸어서 활용한다.

 

 

If 예제

package stream.filter_;

import java.util.ArrayList;
import java.util.List;

public class ForIfFilterExampleMain {

	public static void main(String[] args) {
		List<Integer> integerList = new ArrayList<>();
		
		integerList.add(10);
		integerList.add(20);
		integerList.add(30);
		integerList.add(40);
		integerList.add(50);
		integerList.add(60);
		integerList.add(70);
		
		Integer findNumber = null;
		
		for(int i = 0; i < integerList.size(); i++) {
			System.out.println(integerList.get(i));
			
			if(integerList.get(i).equals(40)) {
				findNumber = integerList.get(i);
				break;
			}
		}
		System.out.println("findNumber = " + findNumber);
	}
}

실행 결과

 

 

string().filter 사용 예제

package stream.filter_;

import java.util.ArrayList;
import java.util.List;

public class FilterExampleMain {

	public static void main(String[] args) {
		
		List<Integer> integerList = new ArrayList<>();
		
		integerList.add(10);
		integerList.add(20);
		integerList.add(30);
		integerList.add(40);
		integerList.add(50);
		integerList.add(60);
		integerList.add(70);
													// 40을 찾는 익명함수
		Integer findNumber = integerList.stream().filter(integer -> {
			System.out.println(integer);
			
			if(integer.equals(40)) {
				return true; // true를 리턴하는게 아니고 필터로 찾던 자료가 맞다는 의미
				
			}
			return false; // false를 리턴하는게 아니고 필터로 찾던 자료가 아니라는 의미
		}).findAny().get();
		System.out.println("findNumber = " + findNumber);
	}
}

실행 결과

 

 

  • map

특정 연산을 적용한 결과를 만들때 사용한다.

특정 요소를 받아서, 어떤 결과를 재저장할지 설정하면 된다.

 

 

map을 사용하지 않은 예제

package stream.map;

import java.util.ArrayList;
import java.util.List;

public class ForMapExampleMain {

	public static void main(String[] args) {
		// [1, 2, 3, 4]가 저장된 리스트를 [10, 20, 30, 40]으로 일괄 변경
		List<Integer> integerList = new ArrayList<>();
	
		integerList.add(1);
		integerList.add(2);
		integerList.add(3);
		integerList.add(4);
	
		List<Integer> x10IntegerList = new ArrayList<>();
		
		for(int i = 0; i < integerList.size(); i++) {
			// 개별 요소를 반복문으로 접근해 10 곱하고 새 리스트에 add
			x10IntegerList.add(integerList.get(i) * 10);
		}
		System.out.println(x10IntegerList);
	}
}

실행 결과

 

 

map 사용 예제

package stream.map;

import java.util.ArrayList;
import java.util.List;

public class MapExampleMain {

	public static void main(String[] args) {
		
		List<Integer> integerList = new ArrayList<>();
		
		integerList.add(1);
		integerList.add(2);
		integerList.add(3);
		integerList.add(4);
	
		List<Integer> x10IntegerList = integerList.stream()
										.map(integer -> integer * 10).toList();
		
		System.out.println(x10IntegerList);

	}
}

실행 결과

 

 

stream API와 Optional

stream()을 옵셔널과 연계하면 더 좋다.

 

 

위와같은 코드를 옵셔널을 쓴다면

 

 

옵셔널을 리턴하는 findAny()와 filter()를 조합한다.

 

옵셔널을 사용한 코드가 과정을 가시적으로 보여주기 때문에 보편적으로는 더 좋다고 생각하는 코드다.

 

스트림은 경우에 따라 다르지만 실제로는 느릴 수 있다.

 

그러나 가독성이 주는 이득이 압도적이기 때문에 가독성을 우선하여 개발 후 성능 필요시 튜닝하는 방향으로 하면 된다.