Lambda Expressions
람다식은 인터페이스에 정의된 객체를 인터페이스를 호출(사용)하는 시점에 사용자가 정의하여 사용할 수 있는 기능을 제공하는 기능입니다. 미리 정의된 메소드, 클래스를 사용하는 것이 아니라 호출 시점에 재정의하여 사용하는 함수적 프로그래밍을 지원하는 기능입니다.
(타입 매개변수) -> {실행문}
(int a) -> { a = a+1 }
람다식을 구현하기 위해서는 먼저 인터페이스에 추상메소드 1개를 선언해야 합니다.
public interface Calculation {
int calculate(int a, int b);
}
추상메소드가 선언한 다음 인터페이스를 호출하는 시점에 아래 코드처럼 Override하여 기능을 정의할 수 있습니다.
Calculation normal = new Calculation() {
@Override
public int calculate(int a, int b) {
return a + b;
}
};
람다식을 사용하면 코드를 훨씬 간단한게 바꿀 수 있습니다.
Calculation lambda = (a,b) -> {return a+b;};
함수적 인터페이스 Functional Interface
람다식이 1개의 메소드만 구현할 수 있기 때문에 두 개 이상의 추상메소드가 선언된 인터페이스에는 람다식을 사용할 수 없습니다. 이렇게 1개의 추상 메소드만 인터페이스를 함수적인터페이스라고 합니다.
@FunctionalInterface 어노테이션을 사용하면 2개 이상의 추상메소드가 선언되지 못하도록 컴파일러가 관리해줍니다.
@FunctionalInterface
public interface Calculation {
int calculate(int a, int b);
}
메소드 참조
보통 메소드를 호출하게 되면 아래처럼 매개 변수를 정의하여 넣어주어야 합니다.
(a, b) -> Class.method(a,b);
위처럼 람다식이 매개변수 값을 넘겨주기만 하고, 바로 메소드를 호출하는 경우는 아래처럼 표현할 수 있습니다.
Class :: method
IntBinaryOperator oper1 = (a,b) -> Math.max(a,b);
IntBinaryOperator oper2 = Math::max;
System.out.println(oper1.applyAsInt(1, 2));
System.out.println(oper2.applyAsInt(1, 2));
2
2
인스턴스 내부의 메소드를 호출하는 경우(ex.getter메소드)에는 아래처럼 표현할 수 있습니다.
instance :: getValue
List<String> list = new ArrayList<>();
Function<List<String>, Integer> consumer1 = s -> s.size();
Function<List<String>, Integer> consumer2 = List::size;
System.out.println(consumer1.apply(list));
System.out.println(consumer2.apply(list));
0
0
생성자 참조
람다식에서 클래스를 생성하여 반환해야 하는 경우는 아래처럼 표현할 수 있습니다.
Class :: new
Function<String, String> fun1 = i -> new String(i);
Function<String, String> fun2 = String :: new;
Java 8 표준 API 함수적 인터페이스
자바 8 부터는 Java.util.function 표준 API 패키지로 함수적 인터페이스를 제공합니다.
Consumer
매개변수(입력값)은 있고, 리턴 값은 없는 함수적 인터페이스입니다. accept() 메소드를 이용해 단지 매개변수를 받아 처리하는 작업만 수행합니다.
인터페이스 | 메소드 | 설명 |
Consumer<T> | accept(T t) | t를 받아 처리함. |
BiConsumer<T, T> | accept(T t1, T t2) | t1가 t2를 받아 처리함. |
DoubleConsumer | accept(Double t) | Double 타입인 t를 받아 처리함. |
IntConsumer | accept(Int t) | Int 타입인 t를 받아 처리함. |
LongConsumer | accept(Long t) | Long 타입인 t를 받아 처리함. |
ObjDoubleConsumer | accept(T t1, Double t2) | t1과 t2를 받아 처리함 |
ObjIntConsumer | accept(T t1, int t2) | t1과 t2를 받아 처리함 |
ObjLongConsumer | accept(T t1, long t2) |
t1과 t2를 받아 처리함 |
Consumer<String> consumer = s -> { System.out.println("출력 : "+s);};
consumer.accept("람다식");
ObjIntConsumer<String> intConsumer = (s, value) -> {System.out.println("출력 : "+s.substring(value));};
intConsumer.accept("람다식",1);
출력 : 람다식
출력 : 다식
Supplier
매개 변수(입력값)은 없고 리턴 값만 있는 함수적 인터페이스입니다.
get() 메소드를 이용해 데이터를 리턴하는 작업을 수행합니다.
인터페이스 | 메소드 | 리턴 | 설명 |
Supplier<T> | get() | T | T를 반환 |
BooleanSupplier | getAsBoolean() | boolean | boolean을 반환 |
DoubleSupplier | getAsDouble() | double | double을 반환 |
IntSupplier | getAsInt() | int | int를 반환 |
LongSupplier | getAsLong() | long | Long을 반환 |
Supplier<String> supplier = () -> {return "람다식";};
System.out.println(supplier.get());
IntSupplier intSupplier = () -> {return 10;};
System.out.println(intSupplier.getAsInt());
람다식
10
Function
매개 변수(입력값)과 리턴값이 있는 함수적 인터페이스입니다.
apply() 메소드를 이용해 입력 값을 받아 매핑하여 값을 리턴합니다.
인터페이스 | 메소드명 | 리턴값 | 설명 |
Function<T, U> | apply(T t) | U | 객체 T를 받아 객체 U로 반환 |
BiFunction<T, U, V> | apply(T t, U u) | V | 객체 T와 객체 U를 받아 객체 V로 반환 |
DoubleFunction<T> | apply(double t) | T | double을 받아 객체 T로 반환 |
IntFunction<T> | apply(int t) | T | int를 받아 객체 T로 반환 |
IntToDoubleFunction | applyAsDouble(int t) | Double | int를 받아 double로 반환 |
IntToLongFunction | applyAsLong(int t) | Long | int를 받아 Long으로 반환 |
LongToDoubleFunction | applyAsDouble(Long t) | Double | Long을 받아 double로 반환 |
LongToIntFunction | applyAsInt(Long t) | Int | Long을 받아 int로 반환 |
ToDoubleBiFunction<T, U> | applyAsDouble(T t, U u) | Double | 객체 T와 객체 U를 받아 double로 반환 |
ToDoubleFunction<T> | applyAsDouble(T t) | Double | 객체 T를 받아 double로 반환 |
ToIntBiFunction<T, U> | applyAsInt(T t, U u) | Int | 객체 T와 객체 U를 받아 int로 반환 |
ToIntFunction<T> | applyAsInt(T t) | Int | 객체 T를 받아 int로 반환 |
ToLongBiFunction<T, U> | applyAsLong(T t, U u) | Long | 객체 T와 객체 U를 받아 long으로 반환 |
ToLongFunction<T> | applyAsLong(T t) | Long | 객체 T를 받아 long으로 반환 |
Function<String, String> function = s -> s+s;
System.out.println(function.apply("람다"));
ToIntFunction<String> toIntFunction = value -> value.length();
System.out.println(toIntFunction.applyAsInt("람다"));
람다람다
2
Function 인터페이스를 사용자 정의 클래스의 input값으로도 사용할 수 있습니다.
static class 학생{
int 아이디;
int 자바점수;
int 데이터베이스점수;
public 학생(int 아이디, int 자바점수, int 데이터베이스점수) {
this.아이디 = 아이디;
this.자바점수 = 자바점수;
this.데이터베이스점수 = 데이터베이스점수;
}
}
public static double 평균(List<학생> 학생들, ToDoubleFunction<학생> function){
double out = 0;
for(학생 학생 : 학생들){
out += function.applyAsDouble(학생);
}
return out/학생들.size();
}
public static void main(String[] args) {
List<학생> 학생들 = new LinkedList<>();
학생들.add(new 학생(1, 100, 80));
학생들.add(new 학생(2, 100, 80));
학생들.add(new 학생(3, 100, 80));
System.out.println("자바평균 : "+평균(학생들, 학생 -> 학생.자바점수));
System.out.println("데이터베이스평균 : "+평균(학생들, 학생 -> 학생.데이터베이스점수));
}
자바평균 : 100.0
데이터베이스평균 : 80.0
Operator
Operator는 매개 변수(입력값)을 가지고 연산을 수행 후 매개변수와 동일한 타입으로 값을 리턴해주는 함수적 인터페이스 입니다. apply() 메소드를 이용해 값을 입력받아 연산을 수행한 결과를 리턴합니다.
인터페이스 | 메소드 | 리턴 | 설명 |
BinaryOperator<T> | apply(T t, T t) | T | T와 T를 이용해 연산 후 T를 리턴 |
UnaryOperator<T> | apply(T t) | T | T를 연산한 후 T를 리턴 |
DoubleBinaryOperator | applyAsDouble(double, double) | double | double 2개로 연산 후 double 반환 |
DoubleUnaryOperator | applyAsDouble(double) | double | double 1개로 연산 후 double 반환 |
IntBinaryOperator | applyAsInt(int, int) | int | int 2개로 연산 후 int 반환 |
IntUnaryOperator | applyAsInt(int) | int | int 1개로 연산 후 int 반환 |
LongBinaryOperator | applyAsLong(long, long) | long | long 2개로 연산 후 long 반환 |
LongUnaryOperator | applyAsLong(long) | long | long 1개로 연산 후 long 반환 |
Predicate
predicate는 매개변수(입력값)과 boolean 리턴 값을 가지는 함수적 인터페이스입니다.
test() 메소드를 이용해 매개변수를 입력받아 처리 후 true/false를 리턴합니다.
인터페이스 | 메소드 | 설명 |
Predicate<T> | test(T t) | 객체 T를 입력받아 판단 후 boolean을 반환 |
BiPredicate<T, U> | test(T t, U u) | 객체 T와 객체 U를 판단 후 boolean을 반환 |
DoublePredicate | test(Double t) | Double 객체를 판단 후 boolean을 반환 |
IntPredicate | test(Int t) | Int 객체를 판단 후 boolean을 반환 |
LongPredicate | test(Long t) | Long 객체를 판단 후 boolean을 반환 |
인터페이스의 순차적 연결
andThen()
andThen은 메소드 A를 호출한 뒤 메소드 B를 순차적으로 호출하는 기능입니다. andThen를 이용해 인터페이스를 엮어 새로운 인퍼페이스를 선언한 뒤 method()를 이용해 실행합니다.
Consumer 인터페이스는 andThen만 허용합니다.
인터페이스C = 인터페이스A.andThen(인터페이스B);
result = 인터페이스C.method();
compose()
compose은 메소드 B를 호출한 뒤 메소드 A를 순차적으로 호출하는 기능입니다. compose를 이용해 인터페이스를 엮어 새로운 인퍼페이스를 선언한 뒤 method()를 이용해 실행합니다.
Function과 Operator 인터페이스는 andThen과 compose를 허용합니다.
인터페이스C = 인터페이스A.compose(인터페이스B);
result = 인터페이스C.method();
and(), or(), negate()
순차적으로 연결할 수 있습니다. and()는 &&, or()은 ||, negate()는 !와 같은 기능을 수행합니다.
인터페이스C = 인터페이스A.and(인터페이스B);
result = 인터페이스C.test();
인터페이스C = 인터페이스A.or(인터페이스B);
result = 인터페이스C.test();
인터페이스C = 인터페이스A.negate();
result = 인터페이스C.test();
참고
신용권, 이것이 자바다, 한빛미디어
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
'JAVA > JAVA' 카테고리의 다른 글
스트림 Stream (0) | 2021.11.07 |
---|---|
Comparator와 Comparable (0) | 2021.10.29 |
Collection 컬렉션 (0) | 2021.10.25 |
제어문(조건문 if, 반복문 for/while) (0) | 2021.10.07 |
연산자 Operator (0) | 2021.10.04 |