organize/자바

Java 35

001cloudid 2024. 8. 22. 12:44
728x90

람다식(Lambda Expression)

  • 함수(메소드)를 간단한 '식(expression)'으로 표현하는 방법
  • 익명 함수(이름이 없는 함수, anonymous function)
  • 함수는 일반적인 용어, 메소드는 객체지향개념 용어. 함수는 클래스에 독립적, 메소드는 클래스에 종속적
  • 람다식은 익명 함수가 아니라 익명 객체임
  • 람다식(익명 객체)을 다루기 위한 참조변수가 필요. 참조변수 타입은? 함수형 인터페이스

람다식 작성하기

int max(int a, int b){
 return a > b ? a : b;
}
  • 1. 메소드의 이름과 반환타입을 제거하고 '->'를 블록{} 앞에 추가
    (int a, int b) -> { return a > b ? a : b; }
  • 2. 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에 ';' 안 붙임)
    (int a, int b) -> a > b ? a : b
  • 3. 매개변수의 타입이 추론 가능하면 생략가능(대부분의 경우 생략가능)
    (a, b) -> a > b? a : b

※주의사항

1. 매개변수가 하나인 경우, 괄호() 생략가능(타입이 없을 때만)
2. 블록 안의 문장이 하나뿐 일 때, 괄호{} 생략가능(끝에 ';' 안 붙임) 단, 하나뿐인 문장이 return문이면 괄호{} 생략불가

 

package chapter14;

public class Study1_1 {

	public static void main(String[] args) {

//		Object obj = (a,b) -> a>b?a:b; //람다식. 익명객체
		Object obj = new Object() {
			int max(int a, int b) {
				return a>b?a:b;
			}
		};
		
//		int value = obj.max(3,5); //에러 발생. Object에 max()메소드를 가지고 있지 않음 → 함수형 인터페이스 사용

	}

}

 

함수형 인터페이스

  • 단 하나의 추상 메소드만 선언된 인터페이스
@FunctionalInterface
interface MyFunction{
	public abstract int max(int a, int b);
}
MyFunction f = new MyFunction(){ //익명클래스. 클래스의 선언, 객체 생성을 동시에
				public int max(int a, int b){
                   return a>b?a:b;
                   }
                };
 int value = f.max(3,5); //MyFunction에 max()가 존재하므로 가능
  • 함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있음
    단, 함수형 인터페이스의 메소드와 람다식의 매개변수 개수와 반환타입이 일치해야함.
MyFunction f = (a,b) -> a>b?a:b;
int value = f.max(3,5); //실제로 람다식(익명 함수)이 호출됨

 

package chapter14;

@FunctionalInterface //함수형 인터페이스는 단 하나의 추상 메소드만 가져야함
interface MyFunction2_1{
	public abstract int max(int a, int b);
}

public class Study2_1 {
	
	public static void main(String[] args) {

//		MyFunction14_2 f = new MyFunction14_2() {
//			@Override
//			public int max(int a, int b) { //오버라이딩 규칙 - 접근제어자는 좁게 할 수 없음
//				return a>b?a:b;
//			}
//		};
		
		//람다식(익명객체)을 다루기 위한 참조변수의 타입은 함수형 인터페이스로 함
		MyFunction2_1 f = (a,b) -> a>b? a:b; //람다식. 익명 객체
		
		int value = f.max(3, 5);
		System.out.println("value = " + value);
		
	}

}

 

함수형 인터페이스 타입의 매개변수, 반환타입

package chapter14;

/**
 * *함수형 인터페이스 타입의 매개변수, 반환타입
 * -함수형 인터페이스 타입의 매개변수
 * void exMethod(MyFunction f){
 * 	f.myMethod(); //MyFunction에 정의된 메소드 호출
 * }
 * @FunctionalInterface
 * interface MyFunction{
 * 	void myMethod();
 * }
 * MyFunction f = () -> System.out.println("myMethod()"));
 * exMethod(f);
 * //두 줄을 한 문장으로
 * exMethod(() -> System.out.println("myMethod()"));
 * -함수형 인터페이스 타입의 반환타입
 * MyFunction myFunction(){
 * 	MyFunction f = ()->{};
 *     return f;
 *     }
 * ==>
 * MyFunction myMethod(){
 * 	return () -> {};
 * }
 */

@FunctionalInterface
interface MyFunction2_2{
	void run(); // == public abstract void run();
}

public class Study2_2 {
	
	static void execute(MyFunction2_2 f) { //매개변수 타입이 MyFunction2_2인 메소드
		f.run();
	}
	
	static MyFunction2_2 getMyFunction2_2() { //반환타입이 MyFunction2_2인 메소드
//		MyFunction2_2 f = () -> System.out.println("f3.run()");
//		return f;
		return ()-> System.out.println("f3.run()");
		
	}

	public static void main(String[] args) {
		
		//람다식으로 MyFunction2_2의 run()을 구현
		MyFunction2_2 f1 = () -> System.out.println("f1.run()");
		
		MyFunction2_2 f2 = new MyFunction2_2() { //익명클래스로 run()을 구현
			@Override
			public void run() { //반드시 public을 붙여야함
				System.out.println("f2.run()");
				
			}
		};
		
		MyFunction2_2 f3 = getMyFunction2_2();
		
		f1.run();
		f2.run();
		f3.run();
		
		execute(f1);
		execute(f2);
		execute(f3);
		execute(()->System.out.println("run()"));
		
	}

}


java.util.function 패키지

package chapter14;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * *java.util.function 패키지
 * -자주 사용되는 다양한 함수형 인터페이스를 제공(표준화에 도움이 됨)
 * java.lang.Runnable : 매개변수도 없고, 반환값도 없음
 * Supplier<T> : 매개변수는 없고 반환값만 있음. 공급자
 * Consumer<T> : 매개변수만 있고 반환값이 없음. 소비자
 * Function<T,R> : 일반적인 함수. 하나의 매개변수를 받아서 결과를 반환
 * Predicate<T> : 조건식을 표현하는데 사용됨. 매개변수는 하나, 반환 타입은 boolean. 조건식
 * -매개변수가 2개인 함수형 인터페이스
 * BiConsumer<T,U> : 두 개의 매개변수만 있고, 반환값은 없음
 * BiPredicate<T,U> : 조건식을 표현하는데 사용됨. 매개변수는 둘, 반환값은 boolean
 * BiFunction<T,U,R> : 두 개의 매개변수를 받아서 하나의 결과를 반환
 * -매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스
 * UnaryOperator<T> : Function의 자손, Function과 달리 매개변수와 결과의 타입이 같음.
 * BinaryOperator<T> : BiFunction의 자손, BiFunction과 달리 매개변수와 결과의 타입이 같음.
 */


public class Study3_1 {

	static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
		List<T> newList = new ArrayList<T>(list.size());

		for(T i : list) {
			newList.add(f.apply(i));
		}	

		return newList;
	}

	static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
		System.out.print("[");
		for(T i : list) {
			if(p.test(i)) //짝수인지 검사
				c.accept(i); //화면에 i 출력
		}	
		System.out.println("]");
	}

	static <T> void makeRandomList(Supplier<T> s, List<T> list) {
		for(int i=0;i<10;i++) {
			list.add(s.get());
		}
	}
	
	public static void main(String[] args) {
		Supplier<Integer>  s = ()-> (int)(Math.random()*100)+1; //1~100난수
		Consumer<Integer>  c = i -> System.out.print(i+", "); 
		Predicate<Integer> p = i -> i%2==0;  //짝수인지 검사
		Function<Integer, Integer> f = i -> i/10*10; // i의 일의 자리를 없앤다.

		List<Integer> list = new ArrayList<>();	
		makeRandomList(s, list); //list를 램덤값으로 채움
		System.out.println(list);
		printEvenNum(p, c, list); //짝수를 출력
		List<Integer> newList = doSomething(f, list);
		System.out.println(newList);

	}

}

 

Predicate의 결합

package chapter14;

import java.util.function.Function;
import java.util.function.Predicate;

/**
 * *Predicate의 결합
 * -and(), or(), negate()로 두 Predicate를 하나로 결합(default 메소드)
 * ※함수형 interface => default메소드, static메소드, 추상메소드
 * //and() : &&, or() : ||, negate() : !
 * Predicate<Integer> p = i -> i < 100;
 * Predicate<Integer> q = i -> i <200;
 * Predicate<Integer> r = i -> i%2==0;
 * Predicate<Integer> notP = p.negate(); //i>=100
 * Predicate<Integer> all = notP.and(q).or(r); // 100<=i && i<200 || i%2==0
 * Predicate<Integer> all2 = notP.and(q.or(r)); // 100<=i && (i<200 || i%2==0)
 * //test()를 이용
 * System.out.println(all.test(2)); //true
 * System.out.println(all2.test(2)); //false
 * -등가비교를 위한 Predicate의 작성에는 isEqual()를 사용(static 메소드)
 * String str1 = "abc";
 * String str2 = "abc";
 * Predicate<String> p = Predicate.isEqual(str1);
 * Boolean result = p.test(str2);
 * //두 줄을 한줄로
 * boolean result = Predicate.isEqual(str1).test(str2);
 */

public class Study4_1 {

	public static void main(String[] args) {

		Function<String, Integer> f = (s) -> Integer.parseInt(s, 16);
		Function<Integer, String> g = (i) -> Integer.toBinaryString(i);
		
		Function<String, String> h = f.andThen(g); // String -> Integer -> String. 함수 두개를 연결.
		Function<Integer, Integer> h2 = f.compose(g);
		
		System.out.println(h.apply("FF")); //"FF" → 255 → "11111111"
		System.out.println(h2.apply(2)); //2 → "10" → 16
		System.out.println("==========================");
		
		Function<String, String> f2 = x -> x; //항등함수(identity function)
		System.out.println(f2.apply("AAA")); //AAA가 그대로 출력
		System.out.println("==========================");
		
		Predicate<Integer> p = i -> i < 100;
		Predicate<Integer> q = i -> i < 200;
		Predicate<Integer> r = i -> i%2==0;
		Predicate<Integer> notP = p.negate(); //!p => i>=100
		
		Predicate<Integer> all = notP.and(q.or(r));
		System.out.println(all.test(150));
		
		System.out.println("==========================");
		
		String str1 = "abc";
		String str2 = "abc";
		
		//str1과 str2가 같은지 비교한 결과를 반환
		Predicate<String> x = Predicate.isEqual(str1);
		boolean result = x.test(str2);
		System.out.println(result);
		
	}

}

 

컬렉션 프레임워크와 함수형 인터페이스

package chapter14;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * *컬렉션 프레임워크와 함수형 인터페이스
 * -함수형 인터페이스를 사용하는 컬렉션 프레임워크의 메소드(와일드카드 생략)
 * Collection 인터페이스
 * => boolean removeif(Predicate<E> fillter) : 조건에 맞는 요소를 삭제
 * List 인터페이스
 * => void replaceAll(UnaryOperator<E> operator) : 모든 요소를 변환하여 대체
 * Iterable 인터페이스
 * => void forEach(Consumer<T> action) : 모든 요소에 작업 action을 수행
 * Map 인터페이스
 * => V compute(K key, BiFunction<K,V,V> f) :  지정된 키의 값에 작업 f를 수행
 * V computeIfAbsent(K key, Function<K,V> f) : 키가 없으면, 작업 f 수행 후 추가
 * V computeIfPresent(K key, BiFunction<K, V, V> f) : 지정된 키가 있을 때, 작업 f 수행
 * V merge(K key, V value, BiFunction<V,V,V> f) : 모든 요소에 병합작업 f를 수행
 * void forEach(BiConsumer<K,V> action) : 모든 요소에 작업 action을 수행
 * void repaceAll(BiFunction<K,V,V>f) : 모든 요소에 치환작업 f를 수행
 */

public class Study4_2 {

	public static void main(String[] args) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		for(int i = 0;i<10; i++)
			list.add(i);
		
		//list의 모든 요소를 출력
		list.forEach(i->System.out.print(i+","));
		System.out.println();
		//람다식이 아닐 경우
		Iterator it = list.iterator();
		while (it.hasNext()) {
			System.out.print(it.next()+" ");
			
		}
		System.out.println();
		
		//list에서 2 또는 3의 배수를 제거
		list.removeIf(x->x%2==0||x%3==0);
		System.out.println(list);
		
		list.replaceAll(i->i*10); //list의 각 요소에 10을 곱함
		System.out.println(list);
		
		Map<String, String> map = new HashMap<String, String>();
		map.put("1", "1");
		map.put("2", "2");
		map.put("3", "3");
		map.put("4", "4");
		
		//map의 모든 요소를 {k,v}의 형식으로 출력
		map.forEach((k,v)->System.out.print("{"+k+", "+v+"}, "));

	}

}

 

메소드 참조(method reference)

package chapter14;

import java.util.function.Function;

/**
 * *메소드 참조(method reference)
 * -하나의 메소드만 호출하는 람다식은 '메소드 참조'로 간단히 할 수 있음 => 클래스이름::메소드이름
 * -static 메소드 참조
 */

public class Study5_1 {

	public static void main(String[] args) {
		
//		Function<String, Integer> f = (String s) -> Integer.parseInt(s);
		Function<String, Integer> f = Integer::parseInt; //메소드 참조
		System.out.println(f.apply("100")+200);

	}

}

 

생성자의 메소드 참조

package chapter14;

import java.util.function.Function;
import java.util.function.Supplier;

/**
 * * 생성자의 메소드 참조
 * -생성자와 메소드 참조
 * Supplier<MyClass> s = () -> new MyClass();
 * Supplier<MyClass> s = MyClass::new;
 * Function<Integer, MyClass> s = (i) -> new MyClass(i);
 * Function<Integer, MyClass> s = MyClass::new;
 * -배열과 메소드 참조
 * Function<Integer, int[]> f = x -> new int[x]; //람다식
 * Function<Integer, int[]> f2 = int[]::new //메소드 참조. 배열타입[]::new
 */

class MyClass5_2{
	int iv;
	
	public MyClass5_2(int iv) {
		this.iv = iv;
	}
}

public class Study5_2 {
	
	public static void main(String[] args) {
		//Supplier 입력 X, 출력 O
//		Supplier<MyClass5_2> s = () -> new MyClass5_2();
//		Supplier<MyClass5_2> s = MyClass5_2::new;

//		MyClass5_2 mc = s.get(); //MyClass5_2 객체 반환
//		System.out.println(mc);
//		System.out.println(s.get()); //또다른 객체
		
		//생성자가 존재한다면, 입력과 출력이 생기므로 Function으로 바뀌어야함
//		Function<Integer,MyClass5_2> f = (i) -> new MyClass5_2(i);
		Function<Integer,MyClass5_2> f = MyClass5_2::new;
		MyClass5_2 mc = f.apply(100);
		System.out.println(mc.iv);
		System.out.println(f.apply(100).iv);
		
		Function<Integer, int[]> f2 = (i)-> new int[i];
		Function<Integer, int[]> f3 = int[]::new;
		System.out.println(f2.apply(200).length);
		System.out.println(f3.apply(300).length);
		
	}

}
728x90

'organize > 자바' 카테고리의 다른 글

Java 37  (0) 2024.08.24
Java36  (0) 2024.08.23
Java 34  (0) 2024.08.22
Java 33  (0) 2024.08.21
Java 32  (0) 2024.08.20