JAVA

27.Java Stream/Immutable

네스이 2022. 9. 21. 17:48

2022.09.20~21.화~수


1.Stream

package dev.syntax.step01;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Getter @Setter
@NoArgsConstructor
@AllArgsConstructor
public class Apple {
	private int weight = 0;
	private String color = "";
	
	@Override
	public String toString() {
		return String.format("Apple {color='%s', weight='%d'}", color, weight);
	}
}
package dev.syntax.step01;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Basic {

	public static void main(String[] args) {
		List<Apple> inventory = Arrays.asList(
				new Apple(80, "green"),
				new Apple(155, "green"),
				new Apple(120, "red"),
				new Apple(75, "brown"),
				new Apple(70, "red")
				);
		
		//Color가 green인 사과만 필터링
		//Java 8 이전 방식
		List<Apple> greenApples = filterGreenApples(inventory);
		System.out.println(greenApples);
		
		List<Apple> heavyApples = filterHeavyApples(inventory);
		System.out.println(heavyApples);
		
		//Java 8 이후 방식 - 메서드 참조를 통한 해결 방식
		List<Apple> greenApples2 = filterApples(inventory, Basic::isGreenApples);
		System.out.println(greenApples2);
		List<Apple> heavyApples2 = filterApples(inventory, Basic::isHeavyApples);
		System.out.println(heavyApples2);
		
		//Java 8 이후 방식 - 람다 : 익명 함수를 통한 해결 방식
		List<Apple> greenApples3 = filterApples(inventory, (Apple apple) -> "green".equals(apple.getColor()));
		System.out.println(greenApples3);
		List<Apple> heavyApples3 = filterApples(inventory, (Apple apple) -> apple.getWeight() > 150);
		System.out.println(heavyApples3);
		//무게가 80 미만이고, 색이 갈색인 사과 필터링
		List<Apple> weiredApples = filterApples(inventory, (Apple apple) -> apple.getWeight() < 80 && "brown".equals(apple.getColor())); 
		System.out.println(weiredApples);
	}
	
	//filter : 특정 항목을 선택, 반환하는 동작
	
	//Java 8 이전 방식
	//모든 녹색 사과만 필터링해서 새로운 리스트로 반환
	public static List<Apple> filterGreenApples(List<Apple> inventory) {
		List<Apple> result = new ArrayList<>();
		for(Apple apple : inventory) {
			if("green".equals(apple.getColor())) result.add(apple);
		}
		return result;
	}
	//150g 초과 사과만 필터링해서 새로운 리스트로 반환
	public static List<Apple> filterHeavyApples(List<Apple> inventory) {
		List<Apple> result = new ArrayList<>();
		for(Apple apple : inventory) {
			if(apple.getWeight() > 150) result.add(apple); //이 줄의 코드만 제외하고 나머지는 filterGreenApples와 다르지 않음
		}
		return result;
	}
	
	//Java 8 이후 방식 - 메서드 참조를 통한 해결 방식
	public static boolean isGreenApples(Apple apple) {
		return "green".equals(apple.getColor());
	}
	
	public static boolean isHeavyApples(Apple apple) {
		return apple.getWeight() > 150;
	}
	
	public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> predictor) {
		List<Apple> result = new ArrayList<>();
		for (Apple apple : inventory) {
			if(predictor.test(apple)) result.add(apple);
		}
		return result;
	}
}

2.Immutable

 -primitive

package dev.syntax.step01primitive;

//원시 타입(기본 자료형, Primitive type)만 필드로 가지고 있는 클래스
public class Cookie {
	private int salt;
	private String butter;
	
	public Cookie(int salt, String butter) {
		this.salt = salt;
		this.butter = butter;
	}
	
	public int getSalt() {
		return salt;
	}
	public void setSalt(int salt) {
		this.salt = salt;
	}
	public String getButter() {
		return butter;
	}
	public void setButter(String butter) {
		this.butter = butter;
	}
	
	
}
package dev.syntax.step01primitive;

public class ImmutableCookie {
	private final int salt;
	private final String butter;
	
	public ImmutableCookie(int salt, String butter) {
		this.salt = salt;
		this.butter = butter;
	}

	public int getSalt() {
		return salt;
	}
	public String getButter() {
		return butter;
	}
}
package dev.syntax.step01primitive;

public class CookieTest {
	public static void main(String[] args) {
		//원시 타입을 필드로 가지고 있는 가변 객체(인스턴스) coo
		Cookie coo = new Cookie(2, "기본 버터");
		
		coo.setSalt(10); //객체 내부의 값을 변경 가능
		System.out.println(coo.getSalt()); //소금맛 쿠기 완성
		
		//원시 타입을 필드로 가지고 있는 불변 객체 kie
		ImmutableCookie kie = new ImmutableCookie(3, "진한 버터");
//		kie.setSalt(20); //객체 내부의 필드값(salt)을 새로운 값으로 변경 불가
		
		//불변 객체는 값의 조회만 가능, 기존의 객체 내부의 값(필드)의 상태는 변경불가
		System.out.println(kie.getButter());
		System.out.println(kie.getSalt());
	}
}

 -reference

package dev.syntax.step02reference;

public class Orange {
	private int sugarLevel; //당도
	private String country; //원산지
	
	public Orange(int sugarLevel, String country) {
		this.sugarLevel = sugarLevel;
		this.country = country;
	}
	
	public int getSugarLevel() {
		return sugarLevel;
	}
	public void setSugarLevel(int sugarLevel) {
		this.sugarLevel = sugarLevel;
	}
	public String getCountry() {
		return country;
	}
	public void setCountry(String country) {
		this.country = country;
	}

	@Override
	public String toString() {
		return "Orange [sugarLevel=" + sugarLevel + ", country=" + country + "]";
	}
}
package dev.syntax.step02reference;

//참조 타입(Orange)
//참조 타입(Orange class)을 필드로 가지고 있는 OrangeCookie 클래스
public class OrangeCookie {
	private final Orange orange;
	private final int salt;
	
	public OrangeCookie(Orange orange, int salt) {
		this.orange = orange;
		this.salt = salt;
	}

	public Orange getOrange() {
		return orange;
	}
	public int getSalt() {
		return salt;
	}
}
package dev.syntax.step02reference;

public class Lemon {
	final private int vitaminLevel; //당도
	final private String country; //원산지
	
	public Lemon(int vitaminLevel, String country) {
		this.vitaminLevel = vitaminLevel;
		this.country = country;
	}
	
	public int getvitaminLevel() {
		return vitaminLevel;
	}
	public String getCountry() {
		return country;
	}
	
	@Override
	public String toString() {
		return "Lemon [vitaminLevel=" + vitaminLevel + ", country=" + country + "]";
	}
}
package dev.syntax.step02reference;

public class LemonCookie {
	private final Lemon lemon;
	private final int salt;
	
	public LemonCookie(Lemon lemon, int salt) {
		this.lemon = lemon;
		this.salt = salt;
	}
	
	public Lemon getLemon() {
		return lemon;
	}
	public int getSalt() {
		return salt;
	}
}
package dev.syntax.step02reference;

public class CookieTest {
	public static void main(String[] args) {
		Orange orange = new Orange(500, "Japan");
		//일본산 오렌지로 만든 오렌지 쿠키 oCoo
		OrangeCookie oCoo = new OrangeCookie(orange, 2);
		
//		oCoo.setSalt(10); // ??
		
		System.out.println("oCoo의 orange 정보 : " + oCoo.getOrange());
		
		//Orange 클래스가 가지고 있는 setXxx()를 통해 미국산 오렌지로 변경
		oCoo.getOrange().setCountry("USA");
		
		System.out.println("oCoo의 orange 정보 : " + oCoo.getOrange());
		//OrangeCookie의 객체 내부의 값이 변경됨
		//-> 불변 클래스(OrangeCookie)가 가지고 있는 멤버 필드로 참조 타입인 Orange를
		//가지고 있다면, 해당 참조 타입의 클래스(Orange)도 불변으로 작성해야 함
		
		//불변 클래스 Lemon, LemonCookie
		Lemon lemon = new Lemon(200, "Italy");
		LemonCookie lemonCookie = new LemonCookie(lemon, 3);
		Lemon lemonOfCookie = lemonCookie.getLemon();
//		lemonOfCookie.setVitaminLevel(); //값 변경 불가
	}
}

 -array

package dev.syntax.step03array;

import java.util.Arrays;

//문자열 배열 타입으로 초콜릿 종류를 필드로 가지고 있는 가변(mutable)클래스
public class Cookie {
	private final String[] chocolates;

	public Cookie(String[] chocolates) {
		this.chocolates = chocolates;
	}

	public String[] getChocolates() {
		return chocolates;
	}

	@Override
	public String toString() {
		return "Cookie [chocolates=" + Arrays.toString(chocolates) + "]";
	}
}
package dev.syntax.step03array;

public class CookieTest {
	public static void main(String[] args) {
		String[] chocolatesArray = { "dark", "milk", "white" };
		Cookie coo = new Cookie(chocolatesArray);
		
		System.out.println("coo의 초콜릿 성분 : " + coo);
		chocolatesArray[0] = "ruby";
		System.out.println("coo의 초콜릿 성분 : " + coo);

		/*
		 * -> new Cookie(chocolatesArray);가 실행 되는 과정에서 
		 * 	  chocolatesArray의 참조값을 복사하여 넘겨주었기 때문(shallow copy, 얕은 복사)
		 * Cookie클래스에서 chocolates 필드를 final로 지정하여 setXxx() 없더라도,
		 * CookieTest라는 외부에서 생성자를 통해 넘겨준 chocolatesArray의 값을 통해 
		 * coo 객체 내부의 필드인 chocolates의 값 변경이 가능한 상태
		 */
		
		//chocolates의 값을 병경하는 다른 방법
		String[] chocolatesFromCoo = coo.getChocolates();
		chocolatesFromCoo[0] = "ganache";
		System.out.println(coo);
	}
}
package dev.syntax.step03array;

import java.util.Arrays;

public class Biscuit {
	private final String[] sugars;

	public Biscuit(String[] sugars) {
//		this.sugars = sugars; //전달받은 sugars의 참조값이 복사되어 외부에서도 변경 가능한 상태가 됨
		this.sugars = Arrays.copyOf(sugars, sugars.length);
	}
	
	public String[] getSugars() {
		//Biscuit 객체가 가진 참조값을 그대로 전달해버리기 때문에
		//외부에서 변경 가능한 상태가 됨
//		return sugars;
//		if(sugars == null) return null;
//		else sugars.clone();
		
		//clone() 배열만 deep copy 나머지 shallow copy
		return (sugars == null) ? null : sugars.clone();
	}

	@Override
	public String toString() {
		return "Biscuit [sugars=" + Arrays.toString(sugars) + "]";
	}
	
}
package dev.syntax.step03array;

public class BiscuitTest {
	public static void main(String[] args) {
		String[] sugarArray = { "White", "Brown", "Dark" };
		
		Biscuit bsc = new Biscuit(sugarArray);
		
		//bsc 객체 내 sugar element값 변경 여부 테스트
		sugarArray[0] = "Crystal";
		System.out.println(bsc);
		//->Arrays.copyOf()를 통해 주소값을 복사하였기 때문에
		//서로 다른 인스턴스를 가리킴
		
		String[] sugarsFromBsc = bsc.getSugars();
		sugarsFromBsc[0] = "Cube";
		System.out.println(bsc);
		
//		Biscuit b = new Biscuit(null);
//		System.out.println(b);
	}
}

 -list

package dev.syntax.step04list;

public class Chocolate {
	private final String cacaoBean;
	private final String cocoButter;
	
	public Chocolate(String cacaoBean, String cocoButter) {
		this.cacaoBean = cacaoBean;
		this.cocoButter = cocoButter;
	}
	
	public String getCacaoBean() {
		return cacaoBean;
	}
	public String getCocoButter() {
		return cocoButter;
	}
	
	@Override
	public String toString() {
		return "Chocolate [cacaoBean=" + cacaoBean + ", cocoButter=" + cocoButter + "]";
	}
}
package dev.syntax.step04list;

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

public class ChocoCookie {
	private final List<Chocolate> chocolates;

	public ChocoCookie(List<Chocolate> chocolates) {
//		this.chocolates = chocolates;
		this.chocolates = new ArrayList<Chocolate>(chocolates);
		//넘겨받은 인수 chocolates를 그대로 사용하지 않고, 새로운 list로 생성하여 초기화
	}
	
	public List<Chocolate> getChocolates() {
//		return chocolates;
		//Java 9 이전, 값의 추가 / 수정이 불가한 리스트
//		return Collections.unmodifiableList(chocolates);
		
		//Java 9 이후 정적(static) 팩토리 메서드
		return List.of(chocolates.toArray(new Chocolate[] {}));
	}

	@Override
	public String toString() {
		return "ChocoCookie [chocolates=" + chocolates + "]";
	}
}
package dev.syntax.step04list;

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

public class CookieTest {
	public static void main(String[] args) {
		Chocolate abc = new Chocolate("아프리카산 카카오콩", "아프리카산 버터");
		Chocolate hershey = new Chocolate("태국산 카카오콩", "태국산 버터");
		
		List<Chocolate> chocolates = new ArrayList<>(Arrays.asList(abc, hershey));
		
		ChocoCookie chikchok = new ChocoCookie(chocolates);
	}
}