본문 바로가기

JAVA/JAVA 8

자바8 람다

반응형

람다

lambda expression 람다 표현식은 나중에 한번 이상 실행할 수 있도록 전달할 수 있는 코드 블록이다.


지금까지 자바에서 코드블록

class Worker implements Runnable {

public void run() {

for(int i=0; i<100; i++) {

doWork();

}

}

}

이 코드를 실행할때 Worker 클래스의 인스턴스를 생성한다.

이 인스턴스를 스레드풀에 넣거나 간단하게 새로운 스레드를 시작한다.


Worker w = new Worker;

new Thread(w).start();


여기에서 핵심은 run 메서드가 별도의 스레드에서 실행하고자 하는 코드를 담고 있다.


커스텀 비교자 custom comparartor를 이용해 정렬

문자열을 기본 사전 순서대로 대신에 길이로 정렬하려고 한다.

sort 메서드에 Comparator 객체를 전달할수 있다.

class LengthComparator implements Comparator<String>() {

public int compare(String first, String second) {

return Integer.compare(first.length(), sencode.length());

}

}


Array.sort(Strings, new LengthCompare());


sort 메서드는 배열이 모두 정렬될때까지 compare 메서드를 계속해서 호출하며 요소들의 순서가 어긋나 있으면 재배치한다.

sort 메서드에(대개는 재구현하지 않을) 요소들을 비교하는데 필요한 코드 조작을 전달하면, 해당코드는 정렬 로직의 일부로 통합된다.


지연실행(deferred execution)의 또 다른 예로 버튼 콜백(callback)

리스너 인터페이스(linstener interface)를 구현하는 클래스의 메서드에 콜백 액션(callback action)을 집어넣고, 인스턴스를 생성해서 버튼에 등록한다. '익명 클래스의 익명 인스턴스' 문법을 사용한다.


button.setOnAction(new EventHandler<ActionEvent>(){

public void handle(ActionEvent event) {

System.out.println('Thanks for clicking!');

}

});


중요한 부분은 handle 메서드 내부에 있는 코드다. 버튼 클릭이 일어날 때마다 실행된다.


코드블록을 어딘가(스레드 풀, sort메서드, 버튼)에 전달한다. 그러면 해당 코드는 얼마 후에 호출된다.

지금까지는 자바에서 어딘가에 코드 블록을 전달하는 일이 쉽지 않았다.

즉 단순히 코드 블록을 전달할 수 없없다.

자바는 객체지향 언어이기 때문에, 원하는 코드가 있는 메서드를 포함하는 클래스의 객체를 생성해야 했다.


다른 언어에서는 코드 블록을 직접 다룰 수 있다.

자바 설계자들은 오랜 시간 이 기능을 추가를 막았다.

결국 자바의 강력한 힘은 단순성과 일관성이다. 만일 언어가 조금 더 간결한 코드를 얻게 해주는 모든 기능을 포함하게 되면 관리하기가 엉망이 될 수 있다.

하지만 코드 블록을 직접 다룰 수 있는 다른 언어에서는 스레드 생성이나 버튼 클릭처리기 등록이 쉬워진 것에 그치지 않고, API의 상당 부분이 더 단순해지고 더 일관성 있고 더  강력해 졌다.

자바에서는 특정 함수를 구현하는 클래스의 객체를 받는 방법으로 유사한 API를 작성할 수 있지만, 이러한 API는 불편할 수 있다.

한동안은 함수형 프로그래밍을 위해 자바를 확장할지 여부가 아니라 자바에서 함수형 프로그래밍을 하는 방법이 문제였다.

자바에 잘 맞는 설계가 나오기까지 수년간 실험이 계속되었다.


람다 표현식 문법


한 문자열이 다른 문자열보다 짧은지 여부를 검사하는 코드를 전달한다.

Integer.compare(first.length(), second.length())


여기서 first와 second는 둘다 문자 문자열이다.

자바는 타입 결합이 강한 언어이기 때문에 타입도 지정해야 한다.


1) 람다 표현식의 한 형태 (파라미터, 화살표 -> 표현식)

(String first, String second) -> Integer.compare(first.length(), second.length())


이와 같은 표현식은 단순한 코드 블록으로, 변수들의 명세와 함게 코드에 전달해야 한다.


2) 표현식 하나로 표현할 수 없는 계산을 수행한다면? 중괄호 {}로 감싸고 명시적인 return 문을 사용한다.

(String first, String second) -> {

if(first.length() < second.length()) return -1;

else if (first.length() > second.length()) return 1;

else return 0;

}


3) 람다 표현식이 파라미터를 받지 않으면, 파라미터 없는 메서드와 마찬가지로 빈 괄호를 사용한다.

() -> { for ( int i=0; i < 100; i++) doWork(); }


4) 람다 표현식의 파라미터 타입을 추정할 수 있는 경우에는 타입을 생략할 수 있다.

Comparator<String> comp = (first, second) -> Integer.compare(first.length(), second.length());

//  (first, second) 가 (String first, String second) 와 같다

여기서 람다 표현식을 문자열 비교자에 대입하기 때문에, 컴파일러는 first와 second가 문자열이라고 추정할 수 있다.


5) 메서드에서 추정되는 타입 한개를 파라미터로 받으면 괄호를 생략할 수 있다.

EventHandler<ActionEvent> listener = event -> System.out.println("Thanks for clicking!");


6) 메서드 파라미터와 마찬가지 방식으로 람다 파라미터에 애노테이션이나 final 수정자를 붙일 수 있다.

(final String name) ->

(@NonNull String name) ->



람다 표현식의 결과 타입은 지정하지 않는다. 결과 타입은 항상 문맥으로부터 추정된다.

예) int 결과를 기대한다.

(String first, String second) -> Integer.compare(first.length(), second.length())


람다 표현식이 어떤 경우에는 값을 리턴하고 다른 경우에는 리턴하지 않는 것은 규칙에 어긋난다.

(int x) -> { if (x>=0) return 1; } 잘못된 것이다.




반응형