Lambda
Java8 이후로 람다 표현식이 생겼고, 그 표현식을 구성하는데 사용되는 화살표 연산자가 생겨났다.
람다 이전의 자바는 Interface를 사용하거나 abstract 클래스를 사용하여 구현해야 되기 때문에 가독성 부분에서 문제가 발생한다.
// 두 수를 더하는 람다식
(a, b) -> System.out.println(a+b)
위와 같은 식에서 일회용이기 때문에 재사용이 불가능하다.
하지만 한번만 쓰고 말 것이라면 굳이 함수를 만들지 않고 사용할 수 있으므로 편리하다.
위 식을 볼때 a,b는 int형인지 어떻게 알수 있을까?
다른 언어에서 내세운 특징인 컴파일러가 스스로 매개 타입 변수가 무엇인지를 알 수 있도록 하는 타입 추론 기능을 사용하였다.
함수형 인터페이스
추상 메소드를 딱 하나만 가지고 있어야 된다.
람다 표현식에서는 함수형 인터페이스의 인스턴스를 만드는 방법으로 사용될 수 있다.
public static void main(String[] args) {
Foo foo = (x) -> System.out.println(x);
}
@FunctionalInterface
public interface Foo{
void printInt(int x);
}
위 방식으로 함수형 인터페이스를 작성할 수 있다.
Lambda Capturing
public supply<Integer> a() {
int seperator = 1;
//seperator = 2 ;//final 키워드가 없더라도 final처럼 사용하지 않으면 문제가 된다.
return () -> seperator;
}
@FunctionalInterface
public interface supply<I extends Number> {
int a();
}
위 코드처럼 단순 지역 변수를 그대로 사용할 수 있다.
하지만, 이렇게 사용할 때에 지역 변수는 effectivly final variable 이여야 한다.
이러한 제약이 있는 이유는 인스턴스 변수는 heap에 저장하고, 지역 변수는 Stack에 저장하는데 람다가 스레드에서 실행된다면 스레드는 지역 변수를 공유할 수 없는 형태이므로 람다가 자유 변수를 참조할 때 자신의 Stack에 복사하여 참조하고 있기 떄문에 수정이 불가능해야 한다.
Lambda 메소드, 생성자, 레퍼런스 참조
메소드 참조
메소드를 참조하여 매개 변수의 정보와 리턴 타입을 알아내어 불필요한 매개 변수를 제거할 수 있다.
(x,y) -> Math.min(x,y);
IntBinaryOperator operator = Math::min; // IntBinaryOperator 인터페이스에 applyAsInt 메소드를 활용하여 연산 값을 리턴 가능하게 만들 수 있다.
정적 메소드와 인스턴스 메소드
정적 메소드는 클래스::메소드 처럼 사용이 가능하고, 인스턴스 메소드는 객체를 생성한 수에 참조변수::메소드 형식으로 사용한다.
public static void main(String[] args) {
ToIntBiFunction<Integer, Integer> function1;
ToIntBiFunction<Integer, Integer> function2;
function1 = cal::staticSum;
System.out.println(function1.applyAsInt(1,2)); // 3 출력
cal c = new cal(); // 인스턴스 메소드는 객체 생성이 우선
function2 = c::instanceSum;
System.out.println(function2.applyAsInt(3,4)); // 7 출력
int result = allSum(function1.applyAsInt(1,2), function2.applyAsInt(3,4)); //매개변수에 메소드 참조 가능
System.out.println(result); // 10 출력
}
public static int allSum(int sum1, int sum2){
return sum1 + sum2;
}
class cal{
public int instanceSum(int n1, int n2){
return n1+n2;
}
public static int staticSum(int n1, int n2){
return n1+n2;
}
}
생성자 참조
단순히 객체를 생성하고 리턴하는 것 뿐이라면 생성자 참조로 변환이 가능하다.
(a, b) -> new cal(a,b);
cal::new; // 변환 가능
단, 생성자가 여러 개 일 경우 매개 변수로 들어오는 개수에 따라 다른 생성자를 접근한다.
public static void main(String[] args) {
Function<Integer, cal> function1 = cal::new;
cal student = function1.apply(1); // 매개변수 1개의 생성자 접근
BiFunction<Integer, Integer, cal> function2 = cal::new;
cal student2 = function2.apply(1,2); // 매개변수 2개의 생성자 접근
}
class cal{
int n1;
int n2;
public cal(int n1, int n2) {
this.n1 = n1;
this.n2 = n2;
}
public cal(int n1){
this(n1, 100);
}
}
아래는 Java.Util.Functinon의 인터페이스를 표시 한 것이다.
| 인터페이스명 | 추상메소드 | 설명 |
| Consumer<\T> | void accept(T t) | 객체 T를 받아 소비 |
| BiConsumer<\T> | void accept(T t, U u) | 객체 T와 U를 받아 소비 |
| DoubleConsumer<\T> | void accept(double value) | double 값을 받아 소비 |
| IntConsumer<\T> | void accept(int value) | int 값을 받아 소비 |
| LongConsumer<\T> | 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 값을 받아 소비 |
'Java' 카테고리의 다른 글
| Java Study 7-2주차 (0) | 2023.11.18 |
|---|---|
| Java Study 7-1주차 (0) | 2023.11.18 |
| Java Study 6-2주차 (0) | 2023.11.11 |
| Java Study 6-1주차 (1) | 2023.11.11 |
| Java Study 5-2주차 (0) | 2023.11.05 |