-
자바 Lambda (람다)자바(Java) 강의/자바 8 2019. 8. 20. 13:16
이번 포스트에서는 자바 8부터 제공하는 Lambda 의 사용법에 대해 알아보도록 한다. 이 포스트는 독자들이 자바의 클래스, 인터페이스, 메서드등 기본을 이미 알고있다고 가정한다.
Lambda (람다)
Lambda(람다)란? 소프트웨어 세계에서 람다란 함수이다. 자바에서는 메서드이다. 그렇다면 모든 함수나 메서드가 람다인가? 아니다. 매개변수로 전해질 수 있는 함수/메서드만을 람다라고 한다. 매개변수로 전해진다는게 무슨뜻일까. 설명을 보며 생각해보아라.
전통적인 자바 프로그램을 떠올려 보자. 메서드를 만들려면 뭐부터 만들어야 할까? 클래스부터 만들어야한다. 실제 산업에서는 인터페이스-인터페이스 구현클래스로 나누어 많이 개발한다. 예를들어서 x + y를 계산하는 Operation 인터페이스가 있다고 하자.
public interface Operation {
Integer add(Integer x, Integer y);
}이 인터페이스를 사용하기 위해서는 이 인터페이스를 구현하는 클래스가 필요하다.
public class OperationImpl implements Operation{
@Override
public Integer add(Integer x, Integer y) {
return x + y;
}
}이제 사용하기 위해 메인에서 초기화를 하고 불러보도록 하자.
public class App {
실행하면 결과 값인 4가 나올것이다.
public static void main(String[] args) {
Operation op = new OperationImpl();
int result = op.add(1, 3);
System.out.println(result);
}
}add 메서드를 사용하기 위해서 개발자는 add 메서드가 존재하는 인터페이스 생성(Operation), 인터페이스 구현(OperationImpl), 그리고 메서드를 사용하기 위해 인터페이스 오브젝트 생성(new OperatonImpl)과정을 거쳤다. 인터페이스 구현과 오브젝트 생성과정을 하나로 합치고, 별도의 OperationImpl.java같은 파일 없이도 인터페이스를 구현할 수 있다면 어떻겠는가? 자바에는 이미 그런 기능이 있다.
public class App {
public static void main(String[] args) {
Operation op = new Operation() {
@Override
public Integer add(Integer x, Integer y) {
return x + y;
}
};
int result = op.add(1, 3);
System.out.println(result);
}
}위처럼 오버라이드 해야하는 메서드의 구현부분을 new 인터페이스이름() { ...바디부분.. }의 바디부분에 넣어주면 된다. 파일만 안 만들었을 뿐이고 구현 부분 클래스의 내부를 작성해야 하는 것은 변함 없다. 람다는 여기에서 조금 더 간단해진 경우이다.
public class App {
public static void main(String[] args) {
Operation op = (x, y) -> { return x + y; }; // @Override public int add(int x, int y) { return x + y; } 와 같은 뜻int result = op.add(1, 3);
System.out.println(result);
}
}new Operation() { @Override.. }하고 구구절절 쓰지 말고, 그냥 깔끔하게 메서드만 구현해서 넘기자는 것이다. 괄호 안에는 인터페이스 구현부분의 파라미터 이름을 개수에 맞게 넣어주고 '->' 다음에는 메서드 바디 부분을 { } 꺽쇠괄호 안에 넣어준다. Operation 인터페이스에서 add메서드의 인자가 2개였기 때문에 x, y 두개를 매개변수로 넣는 것이다. 메서드를 오버라이딩 하는것과 똑같다. 다만 표현방식이 조금 다른것 뿐이다. 리턴 값이 있으므로 리턴을 반드시 해주어야 한다.한줄짜리인 경우 아래처럼 간략하게 쓸 수 있다.
Operation op = (x, y) -> x + y;
Lambda의 제약
이제 의문이 생길 것이다. 만약에 인터페이스에 메서드가 두개 있으면 어떻게 할 것인가? 답은 간단하다. 람다를 사용하지 못한다. 위와 같은 수식으로 인터페이스의 구현부분을 작성할 수 있는 이유는 인터페이스에 구현해야할 메서드가 하나밖에 없기 때문이다. 그렇지 않으면 매개변수의 자료형을 명시하지 않는데 어떤 메서드를 어떻게 맵핑 할 것인가? 구현해야 할 메서드가 하나밖에 없으니, 람다 표현을 쓰기 시작하면 자바가 매개변수는 몇개인지, 타입이 무엇인지 미리 알고있는 상태인 것이다.
Operation op = (x, y, z) -> x + y; // Error:(4, 24) java: incompatible types: incompatible parameter types in lambda expression
위를 시도하면 에러가 날것이다. 또한 y.을 누르면 이미 이 메서드의 두 인자는 Integer임을 알고 있기 때문에 IDE에서도 코드 컴플리션을 제공한다.
printResult((x, y) -> y.compareTo(x));
메서드가 하나인 인터페이스에 밖에 사용하지 못하면 무슨 생각인가? 의외로 실세계에선 1회 사용을 위해 인터페이스를 구현하는 일이 잦다. 또, 반대로 생각하면, 구현할 메서드가 여러개라면 코드 퀄리티와 유지보수를 위해 구현부를 파일로 만드는 것이 적당하다는 뜻일 수도 있다. 다만 기존에는 구현해야할 메서드가 하나밖에 없는 인터페이스임에도 인터페이스 구현 클래스를 하나하나 만들어야 했다. 딱 한 군데서만 쓰고 아무데서도 안쓰더라도 말이다. 자바 8 람다를 이용하면 이런 불편함을 해소할 수 있다. 가장 대표적인 예로 Comparator가 있다. 이제 프린트 부분을 따로 떼어내 보자.
public class App {
public static void main(String[] args) {
Operation op = (x, y) -> x + y;
printResult(op);
}
private static void printResult(Operation op) {
int result = op.add(1, 3);
System.out.println(result);
}
}printResult라는 메서드를 만들고 메서드의 매개변수로 Operation을 넘겨준다. 메인에서는 printResult에 넘겨줄 op 오브젝트를 람다 표현으로 초기화한다. 위에서 매개변수로 전해질 수 있는 함수/메서드를 람다라고 부른다고 했다. 이제 무슨 뜻인지 감이 오는가? 람다를 이용하면 아래같은 방식으로 코드를 간단히 할 수 있다.
public class App {
public static void main(String[] args) {
printResult((x, y) -> x + y);
}
private static void printResult(Operation op) {
int result = op.add(1, 3);
System.out.println(result);
}
}람다 표현을 사용하면 그 자체로 인터페이스의 구현 오브젝트가 생성된다. 람다 표현 자체가 하나의 인터페이스 오브젝트 이므로 이를 매개변수로 넘기는것에도 문제가 없는 것이다.
끝
람다는 Stream과 같이 많이 사용된다. 다음 포스트에서는 Stream을 소개하면서 Lambda를 적용한 예를 더 보도록 한다.
'자바(Java) 강의 > 자바 8' 카테고리의 다른 글
자바8 Optional 활용 1:NullPointException 뿌시기 (3) 2019.01.31 댓글