// 함수형 인터페이스 - 람다식 제공 원리
- 평범한 인터페이스
- 목적: 람다식을 저장하기 위해서 만들어진 인터페이스
1. 표준 API 함수형 인터페이스 > JDK 제공
2. 사용자 정의 함수형 인터페이스 > 개발자가 선언
package com.sorrel012.functionalprogramming;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
//Stream<T> filter(Predicate<? super T> predicate);
//boolean test(T t);
class EvenNumberPredicate implements Predicate<Integer> {
@Override
public boolean test(Integer number) {
return number%2 == 0;
}
}
//void forEach(Consumer<? super T> action);
//void accept(T t);
class PrintlnConsumer implements Consumer<Integer> {
@Override
public void accept(Integer num) {
System.out.println(num);
}
}
//Stream<R> map(Function<? super T, ? extends R> mapper);
//R apply(T t);
class NumberSquareMapper implements Function<Integer, Integer> {
@Override
public Integer apply(Integer num) {
return num*num;
}
}
public class LambdaBehindTheScreensRunner {
public static void main(String[] args) {
List.of(23,43,34,45,36,48).stream()
.filter(n -> n%2==0)
.forEach(System.out::println);
System.out.println();
List.of(23,43,34,45,36,48).stream()
.filter(new EvenNumberPredicate())
.forEach(System.out::println);
System.out.println();
List.of(23,43,34,45,36,48).stream()
.filter(new EvenNumberPredicate())
.forEach(new PrintlnConsumer());
System.out.println();
List.of(23,43,34,45,36,48).stream()
.filter(new EvenNumberPredicate())
.map(n -> n*n)
.forEach(new PrintlnConsumer());
System.out.println();
List.of(23,43,34,45,36,48).stream()
.filter(new EvenNumberPredicate())
.map(new NumberSquareMapper())
.forEach(new PrintlnConsumer());
} //main
}
// 람다식(Lambda Expression)
- 함수(메서드)를 간단한 식(expression)으로 표현하는 방법
-함수형 프로그래밍 방식을 지원하는 표현식
- 람다식을 사용하면 코드 일부가 간결해진다.
- Java 8 출시
- 목적" 컬렉션(배열) 조작
- 자바의 메서드를 표현하는 방법 중 하나로 사용
- 메서드를 대신하는 코드블록
- 자바의 람다식은 인터페이스를 사용해서 만든다.
- 자바의 람다식은 익명 객체(추상 메서드)를 만드는 표현을 간소화시킨 기술이다.
- 익명 객체(익몀ㅇ 함수x) : 함수형 인터페이스 타입의 참조 변수가 필요!
~ 함수형 인터페이스의 메서드와 람다식의 매개변수 개수, 반환타입이 일치해야 함.
- 람다식 작성 규칙
~ 메서드의 이름과 반환 타입을 제거하고 '->'를 블록{} 앞에 추가
~ 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능(끝에 ; 안 붙임.)
~ 매개변수 타입이 추론 가능하면 생략 가능(대부분)
~ 매개변수가 하나인 경우, 소괄호() 생략 가능(타입이 없을 때만!)
~ 블록 안의 문장이 하나일 때, 중괄호{} 생략 가능(끝에 ; 안 붙임.) // return문인 경우 생략 불가
//메서드
int method(int i) {
return (int)(math.random()*5) + 1;
}
//람다식
(i) -> (int)(Math.random()*5) + 1)
int max(int a, int b) {
return a > b ? a : b;
}
//람다식
(a, b) -? a > b ? a : b
int printVar(String name, int i) {
System.out.println(name + "=" + i) ;
}
//람다식
(name, i) -> System.out.println(name + "=" + i)
int square(int x) {
return x * x;
}
//람다식
(x) -> x * x
public class Ex01 {
public static void main(String[] args) {
MyFunction f = (a,b) -> a > b ? a : b;
int value = f.max(3, 5);
System.out.println("value = " + value);
}
}
@FunctionalInterface
interface MyFunction {
int max(int a, int b);
}
@FunctionalInterface
interface MyFunction2 {
void run();
}
public class Ex02 {
static void execute(MyFunction2 f) {
f.run();
}
static MyFunction2 getMyFunction() { //함수형 인터페이스 직접 구현(람다식과 관련x)
MyFunction2 f = () -> System.out.println("f3.run()");
return f;
}
public static void main(String[] args) {
MyFunction2 f1 = () -> System.out.println("f1.run()");
MyFunction2 f2 = new MyFunction2() {
public void run() {
System.out.println("f2.run()");
}
};
MyFunction2 f3 = getMyFunction();
f1.run();
f2.run();
f3.run();
execute(f1);
execute ( () -> System.out.println("run()") );
}
}
package com.test.lambda;
public class Ex87_lambda {
public static void main(String[] args) {
//추상 메서드의 형식에 따라
// - 매개변수 O, X
// - 반환값 O, X
//1. 매개변수 X, 반환값 X //void call();
NoParameterNoReturn pr1 = new NoParameterNoReturn() {
@Override
public void call() {
System.out.println("pr1");
}
};
pr1.call();
NoParameterNoReturn pr2 = () -> {System.out.println("pr2");};
pr2.call();
NoParameterNoReturn pr3 = () -> System.out.println("pr3"); //★★람다식 구현부에 있는 실행 문장이 한 줄이면 {}을 생략할 수 있다.
pr3.call();
System.out.println();
//2-1. 매개변수 한 개 O, 반환값 X //void call(int n);
ParameterNoReturn pr4 = (int n) -> {System.out.println("pr4: " + n);};
pr4.call(100);
pr4.call(200);
pr4.call(300);
ParameterNoReturn pr5 = (n) -> {System.out.println("pr5: " + n);};
pr5.call(400);
ParameterNoReturn pr6 = n -> {System.out.println("pr6: " + n);};
pr6.call(500);
ParameterNoReturn pr7 = n -> System.out.println("pr7: " + n);
pr7.call(600);
System.out.println();
//2-2. 매개변수 여러 개 O, 반환값 X //void call(String name, int age);
MultiParameterNoReturn pr8 = (String name, int age) -> {System.out.println("pr8: " + name + ", " + age);};
pr8.call("홍길동", 20);
MultiParameterNoReturn pr9 = (name, age) -> {System.out.println("pr9: " + name + ", " + age);};
pr9.call("아무개", 25);
System.out.println();
//3. 매개변수 X, 반환값 O //int call();
NoParameterReturn pr11 = () -> {return 100;};
System.out.println("pr11: " + pr11.call());
NoParameterReturn pr12 = () -> 200;
System.out.println("pr12: " + pr12.call());
System.out.println();
//4. 매개변수 O, 반환값 O //int call(int a, int b);
ParameterReturn pr13 = (int a, int b) -> {return a + b;};
System.out.println("pr13: " + pr13.call(10, 20));
ParameterReturn pr14 = (a, b) -> a + b;
System.out.println("pr14: " + pr14.call(20, 30));
}//main
} //Main
interface NoParameterNoReturn {
void call();
}
interface ParameterNoReturn {
void call(int n);
}
interface MultiParameterNoReturn {
void call(String name, int age);
}
interface NoParameterReturn {
int call();
}
interface ParameterReturn {
int call(int a, int b);
}
// java.util.function 패키지
- 자주 사용되는 다양한 함수형 인터페이스를 제공
① java.lang.Runnable : 매개변수x, 반환값x
void run()
② Supplier<T> : 매개변수x, 반환값o
T get()
③ Consumer<T> : 매개변수o, 반환값x
void accept(T t)
④ Function<T,R> : 일반적인 함수. 매개변수1, 반환값 o
R apply(T t)
⑤ Predicate<T> : 조건식을 표현하는 데 사용됨. 매개변수2, 반환값 boolean
⑥ BiConsumer<T,U> : 매개변수2, 반환값x
void accept(T t, U u)
⑦ BiPredicate<T,U> : 조건식을 표현하는 데 사용됨. 매개변수2, 반환값 boolean
boolean test(T t, U u)
⑧ BiFunction<T,U,R> : 매개변수2, 반환값 1
R apply(T t, U u)
⑨ UnaryOperator<T> : Function의 자손. 매개변수와 결과의 타입이 같음.
T apply(T t)
⑩ BinaryOperator<T> : BiFunction의 자손. 매개변수와 결과의 타입이 같음.
import java.util.ArrayList;
import java.util.List;
import java.util.function.*;
public class Ex03 {
public static void main(String[] args) {
Supplier<Integer> s = () -> (int)(Math.random()*100)+1;
Consumer<Integer> c = i -> System.out.print(i + ", ");
Predicate<Integer> p = i -> i % 2 == 0;
Function<Integer, Integer> f = i -> i/10*10; //i의 1의 자리 없애기
List<Integer> list = new ArrayList<>();
makeRandomList(s, list);
System.out.println(list);
printEvenNum(p, c, list);
List<Integer> newList = doSomething(f, list);
System.out.println(newList);
}
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);
}
}
System.out.println("]");
}
static <T> void makeRandomList(Supplier<T> s, List<T> list) {
for(int i = 0; i < 10; i++) {
list.add(s.get());
}
}
}
// Predicate의 결합
- and(), or(), negate() 로 두 Predicate를 하나로 결합(default메서드)
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)
- 등가비교를 위해 isEqual()을 사용(static 메서드)
Boolean result = Predicate.isEqual(str1).test(str2);
import java.util.function.Predicate;
public class Ex04 {
public static void main(String[] args) {
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));
System.out.println("all.test(150) = " + all.test(150));
String str1 = "abc";
String str2 = "abc";
boolean result = Predicate.isEqual(str1).test(str2);
System.out.println("isEqual() 결과 = " + result);
}
}
// 컬렉션 프레임웍과 함수형 인터페이스
- 함수형 인터페이스를 사용하는 컬렉션 프레임웍 메서드(와일드카드 생략)
① Collection - 조건에 맞는 요소를 삭제
boolean removelf(Predicate<E> filter)
② List - 모든 요소를 변환하여 대체
void replaceAll(UnaryOperator<E> operator)
③ Iterable - 모든 요소에 작업 action 수행
void forEach(Consumer<T> action)
④ Map - 지정된 키의 값에 작업 f 수행
V compute(K key, BiFunction<K,V,V> f)
⑤ Map - 키가 없으면 작업 f 수행 후 추가
V computeifAbsent(K key, Function<K,V> f)
⑥ Map - 지정된 키가 있을 때 작업 f 수행
V computeIfPresent(K key, BiFunction<K,V,V> f)
⑦ Map - 모든 요소에 병합작업 f 수행
V merge(K key, V value, BiFunction<V,V,V> f)
⑧ Map - 모든 요소에 작업 action 수행
void forEach(BiConsumer<K,V> action)
⑨ Map - 모든 요소에 치환작업 f 수행
void replaceAll(BiFunction<K,V,V> f)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class Ex05 {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
for(int i = 0; i < 10; i++) {
list.add(i);
}
list.forEach(i -> System.out.print(i + ", ")); //list의 모든 요소 출력
System.out.println();
list.removeIf(x -> x%2 == 0 || x%3 == 0); //list에서 2 또는 3의 배수 제거
System.out.println(list);
list.replaceAll(i -> i * 10); // list의 각 요소에 *10
System.out.println(list);
Map<String, String> map = new HashMap<>();
map.put("1번", "1");
map.put("2번", "2");
map.put("3번", "3");
map.put("4번", "4");
map.forEach((k,v) -> System.out.print("{" + k + ", " + v + "}" + "\n"));
}
}
// 메서드 참조
- 하나의 메서드만 호출하는 람다식은 메서드 참조로 간단히 할 수 있음.
① static 메서드 참조
(x) -> Class명.method(x)
Class명::method
② 인스턴스 메서드 참조
(obj, x) -> obj.method(x)
Class명::method
③ 특정 객체 인스턴스 메서드 참조
(x) -> obj.method(x)
obj::method
④ 생성자의 메서드 참조
Supplier<MyClass> s = () -> new MyClass();
Supplier<MyClass> s = MyClass::new;
import java.util.function.Function;
public class Ex06 {
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);
}
}
- 생성자의 메서드 참조
import java.util.function.Supplier;
public class Ex07 {
public static void main(String[] args) {
Supplier<MyClass> s = MyClass::new;
System.out.println(s.get());
}
}
class MyClass { }
import java.util.function.Supplier;
public class Ex07 {
public static void main(String[] args) {
Supplier<MyClass> s = () -> new MyClass();
Supplier<MyClass> s = MyClass::new;
MyClass mc = s.get();
System.out.println(mc);
System.out.println(s.get());
}
}
class MyClass {
int iv;
MyClass(int iv) {
this.iv = iv;
}
}
import java.util.function.Function;
public class Ex07 {
public static void main(String[] args) {
Function<Integer,MyClass> f = MyClass::new;
MyClass mc = f.apply(100);
System.out.println(mc.iv);
System.out.println(f.apply(200).iv);
}
}
class MyClass {
int iv;
MyClass(int iv) {
this.iv = iv;
}
}
'자바(JAVA)' 카테고리의 다른 글
[자바(Java)] 문자열 <-> 배열 변환 (0) | 2022.12.31 |
---|---|
[자바(Java)] 스트림(stream) (0) | 2022.12.20 |
[자바(Java)] 쓰레드(Thread) (0) | 2022.12.18 |
[자바(Java)] 애너테이션 (0) | 2022.12.17 |
[자바(Java)] 열거형(enum) (0) | 2022.12.17 |