프로그래밍 스펙의 람다
람다식? 익명 함수?
람다식, 람다 함수는 프로그래밍 언어에서 사용되는 용어로, 익명 함수를 지칭한다.
고로 람다 함수는 익명 함수와 같은 의미를 지닌다 라고 생각해도 될 것 같다. 물론 람다가 더 큰 표현이고, 람다의 특징 중 람다 식은 이름을 가질 필요가 없다(익명 함수) 또한 커링 같은 특징들도 있는데, 찾아보면 좋을 것 같다.
일급 함수 ?
함수를 우리가 사용하는 객체 처럼 사용할 수 있다면 일급 함수라고 부른다.
1. 함수를 변수에 할당 할 수 있어야하고
2. 인자에 담을 수 있어야 하고
3. 반환 형으로 사용할 수 있어야 한다.
함수의 시그니처 어디에든 함수가 들어갈 수 있다.
public String(함수로 표현 가능) getString(String[](함수로 표현 가능) args) {
return ""(함수로 표현 가능)
}
위와 같은 스펙을 가진다면 함수는 일급 취급 당한다.
클로저
함수 외부(자신을 둘러 쌓고 있는 가장 가까운 Scope 의 Context)의 자유 변수에 접근 할 수있다.
즉 외부 범위의 변수를 함수 내부에 바인딩 시키는 기술이다.
자바에서는 어떨까?
아쉽게도 자바에서 함수는 일급 취급받지 못한다. 즉 파라미터로 넘기거나, 변수에 담을 수도 없고, 반환 타입으로 사용할 수도 없다.
그래서 자바에서는 함수형 인터페이스라는 것을 제공한다.
일단 함수형 인터페이스는 이후 글에서 보고, 먼저 클로저 부터 보도록 하겠다.
자바에서의 클로저
public class ClosureTest {
private Integer closureB = 2;
private Stream<Integer> calculate(Stream<Integer> stream, Integer closureA) {
return stream.map(new Function<Integer, Integer>() {
@Override
public Integer apply(Integer integer) {
return getInteger(closureA, integer); // getInteger 함수 파라미터로 외부 Context 의 closureA 참조
}
});
}
private Integer getInteger(Integer closureA, Integer integer) {
return integer * closureA + closureB; // getInteger 함수 내부에서 closerB 필드 참조
}
public static void main(String... args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = new ClosureTest()
.calculate(list.stream(), 3)
.collect(Collectors.toList());
System.out.println(result); // [5, 8, 11, 14, 17]
}
}
위의 예제에서는 getInteger 함수가 파라미터로 calculate의 closureA 변수를 참조하고 있고, 함수 내부에서는 필드인 closureB 를 참조하고 있다.
자바에서는 자유변수에 접근해서 그 내용을 바꾸거나 하는 행동을 할 수 없다. 익명함수의 인스턴스로 넘겨지는 모든 변수들은 컴파일러는 기본적으로 final 로 처리한다.
자바 7에서 부터 익명함수 인스턴스를 통해 변수들은 모두 final 이다, 익명 클래스가 필요로하는 변수, Context 들을 복사해서 넘겨주기 때문이다 이를 유사 파이널(effectively final)이라고 한다. 파이널이라고 명시되지는 않았지만, 컴파일러가 봤을 떄는 값의 변경이 없었기 때문에 파이널과 비슷하다 라고 해석한다.
물론 이런 유사 파이널을 우회할 수 있는 방법도 있다.(유사 파이널을 파이널이라고 하겠다)
레퍼런스 변수에 파이널이 붙었기 때문에, 변수의 재할당만 불가능 한 것이지 그 안에 있는 상태는 모두 변경할 수 있기 때문에 자바의 클로저는 반쪽짜리 클로저라고 볼 수 있다. 그럼에도 불구하고 실제로 사용하면 우리에게 많은 이점을 준다.
'Kotlin & Java' 카테고리의 다른 글
동시성 작업 (0) | 2022.03.27 |
---|---|
사례로 알아보는 Generic(제네릭) (3) | 2020.03.22 |
인터페이스의 정적(static) 메서드와 기본(default) 메서드 (0) | 2020.03.02 |
타입 변환과 instanceof 연산자 Tips (0) | 2020.03.02 |
중첩 클래스 와 내부 클래스 (부제 : ViewHolder 를 이너 클래스로 만들때는 왜 static class 로 만들까?) (0) | 2020.03.01 |