30.6K
자바 8이 람다와 함께 등장했다. 늦은 감은 있지만 람다는 프로그래밍 스타일과 전략을 제고하게 할 수도 있는 놀라운 기능이다. 특히 함수형 프로그래밍을 가능하게 해준다.
// Concatenating strings
(String s1, String s2) -> s1+s2;
// Squaring up two integers
(i1, i2) -> i1*i2;
// Summing up the trades quantity
(Trade t1, Trade t2) -> {
t1.setQuantity(t1.getQuantity() + t2.getQuantity());
return t1;
};
// Expecting no arguments and invoking another method
() -> doSomething();
문법이 생소하게 느껴진다면 코드를 다시 한 번 보기 바란다. 처음에는 좀 이상해 보일 수 있는데, 문법에 대해서는 다음 절에서 설명하기로 한다.
input arguments -> body람다 표현은 화살표를 중심으로 두 부분으로 나뉘어진다. 왼쪽은 메소드의 인수이고 오른쪽은 이 인수로 할 일인데 예를 들어 비즈니스 로직 같은 것이다. 본문은 하나의 표현식이거나 코드 블록이고 결과값을 반환할 수도 있다.
@FunctionalInterface public interface IAddable이 인터페이스가 정확히 하나의 추상 메소드를 가지고 있고 @FunctionalInterface라는 주석도 있기 때문에 람다 함수를 위한 타입으로 사용할 수 있다.{ // To add two objects public T add(T t1, T t2); }
// Our interface implementations using Lambda expressions // Joining two strings?note the interface is a generic type IAddableIAddable이 범용 타입 인터페이스이므로 위의 예에서와 같이 각기 다른 타입을 더할 때 사용할 수 있다.stringAdder = (String s1, String s2) -> s1+s2; // Squaring the number IAddable square = (i1, i2) -> i1*i2; // Summing up the trades quantity IAddable tradeAdder = (Trade t1, Trade t2) -> { t1.setQuantity(t1.getQuantity() + t2.getQuantity()); return t1; };
// A lambda expression for adding two strings. IAddable계속하기 전에 지금까지의 내용을 정리해보자. 중요한 점은 비즈니스 로직을 여기저기로 전달할 수 있는 함수의 형태로 다루게 된다는 것이다. 이전과 달리 클래스를 만들지 않고도 비즈니스 로직의 다양한 변형을 순식간에 정의할 수 있다.stringAdder = (s1, s2) -> s1+s2; // this method adds the two strings using the first lambda expression private void addStrings(String s1, String s2) { log("Concatenated Result: " + stringAdder.add(s1, s2)); }
public void testPreJava8() {
IAddable tradeMerger = new IAddable() {
@Override
public Trade add(Trade t1, Trade t2) {
t1.setQuantity(t1.getQuantity() + t2.getQuantity());
return t1;
}
};
}
여기에서 인터페이스를 사용하는 클래스를 만들고 클래스 객체에 더하는 메소드를 적용하였다.
IAddable addable = ....; Trade t1 = new Trade(1, "GOOG", 12000, "NEW"); Trade t2 = new Trade(2, "GOOG", 24000, "NEW"); // using the conventional anonymous class.. Trade mergedTrade = tradeMerger.add(t1,t2);비즈니스 로직은 기술적인 목적의 세부 사항들과 얽혀있고 핵심 로직은 클래스 구현과 밀접하게 연관되어 있다. 예를 들어 위에서 합쳐진 거래를 반환하는 대신 두 거래 중 큰 건을 반환해야 한다면 한숨을 한 번 쉬고 커피를 한 모금 마시고, 끙 소리도 낸 후 팔을 걷어 부치고 코드를 다시 쓸 준비를 해야 할 것이다.
// Summing up the trades quantity IAddable여기를 보면 각각의 함수에 대해 람다를 선언하였고 메소드는 다음과 같이 람다에 맞도록 만들어졌다.aggregatedQty = (t1, t2) -> { t1.setQuantity(t1.getQuantity() + t2.getQuantity()); return t1; }; // Return a large trade IAddable largeTrade = (t1, t2) -> { if (t1.getQuantity() > 1000000) return t1; else return t2; }; // Encrypting the trades (Lambda uses an existing method) IAddable encryptTrade = (t1, t2) -> encrypt(t1,t2);
//A generic method with an expected lambda
public void applyBehaviour(IAddable addable, Trade t1, Trade t2){
addable.add(t1, t2);
}
메소드는 충분히 범용적이라 주어진 람다 표현(IAddable 인터페이스)을 써서 임의의 두 거래에 적용할 수 있다.
// The functional interface
@FunctionalInterface
public interface Runnable {
public void run();
}
// example implementation
new Thread(new Runnable() {
@Override
public void run() {
sendAnEmail();
}
}).start();
보면 알겠지만 이런 방식의 익명 클래스 생성과 사용은 매우 장황하고 보기에 안 좋다. 위의 메소드에서 sendAnEmail()를 제외하면 나머지는 반복적이고 틀에 박힌 코드이다.
// The constructor now takes in a lambda new Thread( () -> sendAnEmail() ).start();위에서 강조된 람다 표현 () → sendAnEmail()은 쓰레드의 생성자로 넘겨진다. 이 표현식은 (새 쓰레드에서 항상 이메일을 보내는 등의) 어떤 행동을 전달하는 실제 코드 (Runnable의 인스턴스)임에 주의하자.
public class AsyncManager{
public void runAsync(Runnable r) {
new Thread(r);
}
}
클라이언트는 요구 사항에 맞춰 많은 람다 표현을 만들어낼 수 있다. 예를 들어 다음과 같이 서버측 클래스에 전달될 수 있는 다양한 람다 표현이 있다.
manager.runAsync(() -> System.out.println("Running in Async mode"));
manager.runAsync(() -> sendAnEmail());
manager.runAsync(() -> {
persistToDatabase();
goToMoon();
returnFromMars();
sendAnEmail();
});
요약
댓글