[ 람다함수 ]
프로그래밍 언어에서 사용되는 개념으로 익명 함수(Anonymous functions)를 지칭하는 용어
익명 함수들은 어느 언어에서나 일급 객체라는 특징을 가짐
일급 객체
다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체
를 가리킨다. 보통 함수에 인자로 넘기기, 수정하기, 변수에 대입하기와 같은 연산을 지원할 때 일급 객체라고 한다.
[ 특징 ]
람다 대수는 이름을 가질 필요가 없고
두 개 이상의 입력이 있는 함수는 1개의 입력만 받는 람다 대수로 단순화 될 수 있다 - 커링
람다 실행블록에는 클래스의 필드와 메서드를 제약없이 사용 가능하다
람다식 내에서 사용되는 지역변수는 final이 없어도 상수로 간주된다
람다식으로 선언된 변수명은 다른 변수명과 중복될 수 없다
람다 대수
추상화와 함수 적용 등의 논리 연산을 다루는 형식 체계로 함수를 보다 단순하게 표현하는 방법이다
커링
f(a,b,c) → f(a)(b)(c)
다중 인수를 갖는 함수를 단일 인수를 갖는 함수들의 함수열로 바꾸는 것을 말한다.단일 호출로 처리하는 함수를 각각의 인수가 호출 가능한 프로세스로 호출된 후 병합되도록 변환하는 것이다
[ 장점 ]
코드의 간결함
가독성의 향상 : 개발자의 의도가 명확히 드러난다
생산성의 향상 : 함수를 만드는 과정없이 한번에 처리할 수 있다
병렬처리에 용이 : 멀티쓰레드 활용
[ 단점 ]
호출의 까다로움
불필요한 람다의 남발 시 가독성이 떨어진다
디버깅이 어렵다
람다 stream 사용 시 단순 for문 혹은 while문 사용 시 성능이 떨어진다
[ 사용하기 ]
매개변수 -> 함수 몸체
int a(int x, int y) {
return x < y ? x : y;
}
(x, y) -> x < y ? x : y;
//함수 몸체가 단일 실행문이면 중괄호{} 생략 가능
//return문으로만 구성되어 있으면 생략 불가능
(int x) -> x+1
(x) -> x+1
x -> x+1
(int x) -> { return x+1; }
x -> { return x+1; }
//기존 자바 문법
new Thread(new Runnable() {
public void run() {
System.out.println("전통적인 방식의 일회용 스레드 생성");
}
}).start();
//람다식 문법
new Thread(()->{
System.out.println("람다 표현식을 사용한 일회용 스레드 생성");
}).start();
[ 함수형 인터페이스( Functional Interface ) ]
Java는 기본적으로 객체지향 언어이기 때문에 순수 함수와 일반 함수를 다르게 취급하고 있으며 Java에서는 이를 구분하기 위해 함수형 인터페이스가 등장하게 되었다
함수형 인터페이스란 함수를 1급 객체처럼 다룰 수 있게 해주는 어노테이션으로, 인터페이스에 선언하여 단 하나의 추상 메소드만을 갖도록 제한하는 역할을 한다. 함수형 인터페이스를 사용하는 이유는 Java의 람다식이 함수형 인터페이스를 반환하기 때문이다
하지만 함수형 인터페이스의 등장으로 우리는 함수를 변수처럼 선언할 수 있게 되었다
함수형 인터페이스를 구현하기 위해서는 인터페이스를 개발하여 그 내부에는 1개 뿐인 abstract 함수를 선언하고, 위에는 @FunctionalInterface 어노테이션을 붙여주면 된다
람다식으로 생성된 순수 함수는 함수형 인터페이스로만 선언이 가능하다. 또한 @FunctionalInterface는 해당 인터페이스가 1개의 함수만을 갖도록 제한하기 때문에, 여러 개의 함수를 선언하면 컴파일 에러가 발생한다
@FunctionalInterface
public interface LambInter {
void test(int a);
}
public class Lambda {
public static void main(String[] args) {
LambInter lam = null;
lam = (a) ->{
int result = a*30;
System.out.println("람다" + result);
};
lam.test(30);
}
}
//Console
//람다900
[ JAVA에서 제공하는 함수형 인터페이스 ]
자바8부터는 빈번하게 사용되는 함수형 인터페이스(Functional Interface)는 java.util.function 표준 API 패키지로 제공된다
이 패키지에서 제공하는 함수적 인터페이스의 목적은 메소드 또는 생성자의 매개 타입으로 사용되어 람다식을 대입할 수 있도록 하기 위해서이다
- Consumer
- Supplier
- Function
- Operator
- Predicate
1. Consumer
파라미터만 있고 리턴값이 없는 추상 메서드를 가진다
추상메서드 accept를 호출하여 사용
인터페이스 | 추상 메서드 | 설명 |
Consumer<T> | void accept(T t) | 객체 T를 받아 소비 |
BiConsumer<T, U> | void accept(T t, U u) | 객체 T와 U를 받아 소비 |
DoubleConsumer | void accept(double value) | double 값을 받아 소비 |
IntConsumer | void accept(int value) | int 값을 받아 소비 |
LongConsumer | void accept(long value) | long 값을 받아 소비 |
ObjDoubleConsumer<T> | void accept(T t, double value) | 객체 T와 double 값을 받아 소비 |
ObjIntConsumer<T> | void accept(T t, int value) | 객체 T와 int 값을 받아 소비 |
ObjLongConsumer<T> | void accept(T t, long value) | 객체 T와 long 값을 받아 소비 |
import java.util.function.Consumer;
import java.util.function.BiConsumer;
import java.util.function.DoubleConsumer;
import java.util.function.ObjIntConsumer;
public class Lambda {
public static void main(String[] args) {
Consumer<String> consumer = (t) ->{
System.out.println("디파일러 컨슘" + t);
};
consumer.accept("ㅇㅇㅇㅇ");;
BiConsumer<String, String> bi = (a,b) -> {
System.out.println("ㅋㅋㅋㅋ" + a + "ㅋㅋㅋㅋ" + b);
};
bi.accept("어제 길을 가는데", "누가 넘어짐");
DoubleConsumer dc = (dd) ->{
System.out.println("내가 지금 사용하는 자바 버전은? " + dd);
};
dc.accept(1.8);
ObjIntConsumer<String> obc = (i, j) ->{
System.out.println(i + j);
};
obc.accept("자바", 8);
}
}
//Console
디파일러 컨슘 지이이잉
ㅋㅋㅋㅋ 어제 길을 가는데 ㅋㅋㅋㅋ 누가 넘어짐
내가 지금 사용하는 자바 버전은? 1.8
자바8
2. Supplier
파라미터는 없고 리턴값만 있다
get....() 메서드를 호출하여 실행한 후 호출한 곳으로 데이터를 리턴한다
인터페이스 | 추상 메서드 | 설명 |
Supplier<T> | T get() | T 객체를 리턴 |
BooleanSupplier | Boolean getAsBoolean() | Boolean 값을 리턴 |
DoubleSupplier | double getAsDouble() | double 값을 리턴 |
IntSupplier | int getAsInt() | int 값을 리턴 |
LongSupplier | long getAsLong() | long 값을 리턴 |
import java.util.function.Supplier;
import java.util.function.IntSupplier;
public class Lambda {
public static void main(String[] args) {
Supplier<String> supplier = () ->{
String study = " 나는 오늘도 열심히 자바 공부를 한다";
return study;
};
System.out.println(supplier.get());
IntSupplier lotto = () ->{
int num = (int)(Math.random()*45)+1;
return num;
};
}
}
//Console
나는 오늘도 열심히 자바 공부를 한다
로또 가즈아 : 9
3. Function
매개값과 리턴값이 있는 apply....()메서드를 사용
매개값을 리턴으로 매핑한다
인터페이스 | 추상 메서드 | 설명 |
Function<T, R> | R apply(T t) | 객체 T를 객체 R로 매핑 |
BiFunction<T, U, R> | R apply(T t, U u) | 객체 T와 U를 객체 R로 매핑 |
DoubleFunction<R> | R apply(double value) | double을 객체 R로 매핑 |
IntFunction<R> | R apply(int value) | int을 객체 R로 매핑 |
IntToDoubleFunction | double applyAsDouble(int value) | int를 double로 매핑 |
IntToLongFunction | long applyAsLong(int value) | int를 long으로 매핑 |
LongToDoubleFunction | double applyAsDouble(long value) | long을 double로 매핑 |
LongToIntFunction | int applyAsInt(long value) | long을 int로 매핑 |
ToDoubleBiFunction<T, U> | double applyAsDouble(T t, U u) | 객체 T와 U를 double로 매핑 |
ToDoubleFunction<T> | double applyAsDouble(T value) | 객체 T를 double로 매핑 |
ToIntBiFunction<T, U> | int applyAsInt(T t, U u) | 객체 T와 U를 int로 매핑 |
ToIntFunction<T> | int applyAsInt(T t) | 객체 T를 int로 매핑 |
ToLongBiFunction<T, U> | long applyAsLong(T t, U u) | 객체 T와 U를 long으로 매핑 |
ToLongFunction<T> | long applyAsLong(T t) | 객체 T를 long으로 매핑 |
Function<Student, String> function = t -> {
return t.getName();
};
Function<Student, String> function = t -> t.getName();
4. Operator
파라미터, 리턴값이 모두 있는 추상 메서드 apply....()를 가지고 있다
파라미터 값을 연산하고 그 결과를 리턴 시 사용할 수 있다
인터페이스 | 추상 메서드 | 설명 |
BinaryOperator<T> | BiFunction<T,U,R>의 하위 인터페이스 | T와 U를 연산 후 R 리턴 |
UnaryOperator<T> | Function<T, R>의 하위 인터페이스 | T를 연산한 후 R 리턴 |
DoubleBinaryOperator | double applyAsDouble(double, double) | 두 개의 double을 연산 |
DoubleUnaryOperator | double applyAsDouble(double) | 한 개의 double을 연산 |
IntBinaryOperator | int applyAsInt(int, int) | 두 개의 int를 연산 |
IntUnaryOperator | int applyAsInt(int) | 한 개의 int를 연산 |
LongBinaryOperator | long applyAsLong(long, long) | 두 개의 long을 연산 |
LongUnarayOperator | long applyAsLong(long) | 한 개의 long을 연산 |
import java.util.function.BinaryOperator;
public class Lambda {
static int[] arr = {13, 15, 100, 1, 37, 76};
public static void main(String[] args) {
IntBinaryOperator operMin = (a, b) ->{
return Math.min(a, b);
};
int min = maxOrMin(operMin);
IntBinaryOperator operMax = (a, b) ->{
return Math.max(a, b);
};
int max = maxOrMin(operMax);
System.out.printf("최소값 : %d, 최대값 : %d", min, max);
}
public static int maxOrMin(IntBinaryOperator oper){
int result = arr[0];
for(int num : arr) {
result = oper.applyAsInt(result, num);
}
return result;
}
}
//Console
최소값 : 1, 최소값 : 100
5.Predicate
파라미터를 조사하여 truse / false값을 리턴하는 test....()메서드를 가지고 있다
인터페이스 | 추상 메서드 | 설명 |
Predicate<T> | Boolean test(T t) | 객체 T를 조사 |
BiPredicate<T, U> | Boolean test(T t, U u) | 객체 T와 U를 비교 조사 |
DoublePredicate | Boolean test(double value) | double 값을 조사 |
IntPredicate | Boolean test(int value) | int 값을 조사 |
LongPredicate | Boolean test(long value) | long 값을 조사 |
package java1222;
public class Ourclass {
private String name;
private String gender;
private int score;
public Ourclass(String name, String gender, int score) {
this.name = name;
this.gender = gender;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
import java.util.ArrayList;
import java.util.function.Predicate;
public class PredicateExam {
public static ArrayList<Ourclass> arrayList = new ArrayList<>();
public static void main(String[] args) {
arrayList.add(new Ourclass("김범수", "남자", 30));
arrayList.add(new Ourclass("나얼", "남자", 50));
arrayList.add(new Ourclass("박효신", "남자", 60));
arrayList.add(new Ourclass("이수", "남자", 55));
arrayList.add(new Ourclass("아이유", "여자", 42));
arrayList.add(new Ourclass("김연우", "남자", 30));
arrayList.add(new Ourclass("박화요비", "여자", 25));
arrayList.add(new Ourclass("이선희", "여자", 20));
arrayList.add(new Ourclass("이승기", "남자", 95));
arrayList.add(new Ourclass("허각", "남자", 80));
Predicate<Ourclass> female = t ->{
System.out.print(t.getName());
System.out.println(t.getScore());
return t.getGender().equals("여자");
};
Predicate<Ourclass> male = t ->{
System.out.print(t.getName());
System.out.println(t.getScore());
return t.getGender().equals("남자");
};
double fe = avg(female);
System.out.println("여성 평균 : " + fe);
double ma = avg(male);
System.out.println("남성 평균 : " + ma);
}
public static double avg(Predicate<Ourclass> score){
int count = 0;
int sum = 0;
for(Ourclass clsMate : arrayList) {
if(score.test(clsMate)) {
count++;
sum += clsMate.getScore();
}
}
return (double)sum/count;
}
}
//Console
여성 평균 : 29.0
남성 평균 : 57.142857142857146
참조
https://palpit.tistory.com/673
https://mangkyu.tistory.com/113
https://khj93.tistory.com/entry/JAVA-%EB%9E%8C%EB%8B%A4%EC%8B%9DRambda%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%82%AC%EC%9A%A9%EB%B2%95
2023.04 ~ 백엔드 개발자의 기록
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!