사용자 도구

사이트 도구


java:8

Java 8

Lambda

@Reapeatable

@Repeatable(Annas.class)
@interface Anna { int value(); }
 
@interface Annas {
    Anna[] value();
}
 
@Anna(1)
@Anna(2)
class JavaBeat{}
 
// -> 자동으로 아래 형태로 변환
@Annas({@Anna(1), @Anna(2)})
class JavaBeat{}

Stamped Lock

What's new

Lambda

이일민님의 Java 8 람다에 관한 정리

자바 람다식 정리해보자.

컴파일러에 의해서 람다식 바디는 람다식이 정의된 클래스의 메소드로 변환된다. 람다식이 위치한 자리에는 invokedynamic 명령이 들어간다. 람다 indy의 부트스트랩은 람다 메타팩토리 메소드다.

람다 메타 팩토리에는 람다가 구현하고 있는 함수형 인터페이스(SAM 타입)와 메소드로 변환된 람다 바디를 가리키는 메소드 핸들, 람다가 캡처할 외부 변수 정보 등이 전달된다.

람다 메타팩토리는 이름 그대로 람다 팩토리를 만드는 팩토리이다. 람다 메타팩토리는 indy의 부트스크랩 메소드로 동작해서 람다 팩토리에 대한 콜사이트를 리턴한다.

람다 팩토리는 람다 오브젝트 또는 펑션 오브젝트라고 불리는 람다 메소드 호출을 포장한 오브젝트를 생성한다. 캡처할 변수가 없는 무상태 람다인 경우는 매번 동일한 오브젝트를 리턴하고, 매번 다른 변수를 캡처해서 새로운 람다를 만들어야 하는 경우는 스태틱 팩토리 메소드(get$Lambda)를 호출해서 람다 오브젝트를 생성한다.

람다 오브젝트는 메타팩토리에서 동적으로 정의하는, 함수형 인터페이스를 구현한 내부 클래스의 인스턴스이다. 클래스 생성 방식은 익명함수와 유사하다. 다른 점은 내부에 람다 바디가 들어가지 않고, indy를 통해서 전달된 디슈가된 람다 메소드에 대한 메소드 핸들을 호출하거나 람다 바디 대신 제공된 참조 메소드를 호출하는 기능만 담당한다. 캡처되는 변수는 내부 필드에 저장됐다가 람다 메소드에 대한 메소드 핸들에 주입된다. 익명 클래스와 다른 점은 항상 this를 전달 받지 않고 this를 사용하는 경우에만(무상태가 아닌 경우만) this 필드를 가진다.

람다식을 Indy로 전환하는 이유는? 람다 팩토리를 생성하는 부트스트래핑 과정 바이트코드에 고정하지 않고 언어 런타임(API)에 위임해서 향후 바이트코드 호환성을 유지하면서 람다 팩토리 전략을 발전시킬 수 있게 하기 위해서이다. 향후 디슈가된 람다 메소드를 가리키는 메소드 핸들을 람다 오브젝트를 내부 클래스로 따로 정의하지 않고도 박싱에 의해서 샘 타입으로 사용되게 할 수 있는 자바 버전이 나오면, 람다 메타팩토리를 그 방식으로 전환하기만 하면 된다. 그러면 자바8에서 컴파일된 람다를 사용하는 코드를 재컴파일 없이 그대로 새로운 람다 팩토리를 사용하는 버전에서도 사용할 수 있다. 당연히 반대도 가능하다.

아직까지는 내부 클래스를 최적화된 방식으로(재사용 가능한 경우 매번 인스턴스를 만들지 않아도 되도록, 익명 클래스를 정적 변수에 정의해서 재사용하는 것과 유사하게) 익명 클래스처럼 만들고, 람다 바디는 메소드 형태로 분리하고 위임해서 동작하도록 만드는 정도이다.

자바8 람다는 람다를 번거롭게 보이는 SAM 타입으로 포장해서라도 과거 버전 라이브러리를 재사용할 수 있게 만들고, Indy-메타팩토리를 이용해 미래 버전과의 바이트코드 호환성을 지키려는 노력이 잘 담겨있는 꽤나 정성스러운 작업의 결과물인 듯 하다. 까짓거 새로운 언어 만든다 생각하고 바이트코드, 라이브러리 호환성 다 깨버리고 폼나는 언어적인 확장을 하면 그만이겠지만 왜 안 그랬는지 생각해보자. 왜 언어의 변화는 10년에 한번만 하겠다고 공언하는지도. 자바8 잘 공부하면 적어도 10년은 더 먹고 살 수 있을 듯.
  • -Djdk.internal.lambda.dumpProxyClasses 람다를 클래스로 만들어서 떨궈줌
java/8.txt · 마지막으로 수정됨: 2017/12/12 10:42 저자 kwon37xi