본문 바로가기
프로그래밍/Java

[Java 8] 람다 표현식과 주의 사항

by 사바라다 2019. 11. 13.

안녕하세요. 오느른 JAVA8에 관하여 실습해보도록 하겠습니다. 먼저 한가지 짚고 넘어가도록 하겠습니다. JAVA 8에서 이루고자 했던 것은 무엇일까요? 제가 일반적으로 많이들 말하는 부분이 간결한 코드와 멀티코어 프로세서를 쉽게 사용할 수 있도록 하는 것이라고 합니다. 그중 간결성에 대해서는 여타 많은 언어들이 나오면서 JAVA는 배우기 힘든 언어가 되었습니다. 이러한 단점을 극복하고자 Optional, Funtion, Lambda 등 다양한 방법이 JAVA8에 나왔습니다. 오늘부터는 이러한 기능과 방법을 알아보도록하겠습니다.

lambda식 예제와 Function

lambda식의 사용예제를 보도록 하겠습니다.

@FunctionalInterface
public interface Foo {
    String method(String string);
}

public String add(String string, Foo foo) {
    return foo.method(string);
}

위와 같은 인터페이스와 메서드가 정의되어 있다고 하겠습니다. 이럴경우 클라이언트가 사용할 때에는 아래와 같이 사용할 수 있습니다.
useFoo는 add 메서드를 구현한 class입니다.

public static void main(String[] args) {
    Foo foo = new Foo() {
        public String method(String string) {
            return string + "Hello";
        }
    };
    String result = useFoo.add("Hello", foo);
    System.out.println(result);
}

람다식을 사용해 표현식을 변경해 보겠습니다.

public static void main(String[] args) {
    Foo foo = param -> param + "Hello";
    String result = useFoo.add("Hello", foo);
    System.out.println(result);
}

훨씬 간결해진 것을 확인할 수 있습니다.

위의 interface를 보면 단지 String을 다른 String으로 변환할 뿐입니다. 이럴때는 interface를 새로 만들 필요 없이 java8에서 제공하는 Funtional 인터페이스의 하나인 Function<T,R>을 사용하면 됩니다. 위의 예제를 Function<T,R>을 사용하여 변경하면 아래와 같습니다.

public String add(String string, Function<String, STring fn>) {
    return fn.apply(string);
}

public static void main(String[] args) {
    Function<String, String> foo = param -> param + "Hello";
    String result = useFoo.add("Hello", foo);
    System.out.println(result);
}

별도의 인터페이스를 생성할 필요가 없는 것이지요.

Lambda 식을 사용할 때 주의사항

메서드 이름을 같게하고 파라미터만 다르게 하는  오버라이딩은 피하는게 좋습니다.

public interface Processor {
    String process(Callable<String> c) throws Exception;
    String process(Supplier<String> s);
}

public class ProcessorImpl implements Processor {
    @Override
    public String process(Callable<String> c) throws Exception {
        // implementation details
    }

    @Override
    public String process(Supplier<String> s) {
        // implementation details
    }
}

위와같이 예제가 있습니다. 위와 같을 경우 lambda식을 어떻게 각각 사용할 수 있을까요?

String result = processor.process(() -> "abc");

이렇게 했을경우 정상동작하지 않습니다. 왜그럴까요?

reference to process is ambiguous
both method process(java.util.concurrent.Callable<java.lang.String>) 
in com.baeldung.java8.lambda.tips.ProcessorImpl 
and method process(java.util.function.Supplier<java.lang.String>) 
in com.baeldung.java8.lambda.tips.ProcessorImpl match

에러로그를 봤을때 2개의 메서드 중 어떤 것을 사용해야할 지 모호하기 때문입니다.

이러한 문제를 해경하는 방법으로는 2가지가 있습니다. 첫번째는 이름을 변경하는것이고, 두번째는 casting값을 명시해주는 것입니다.

일반적으로는 앞의 이름을 변경하는 것이 좀 더 직관적이로 잘 쓰입니다.

람다안에 Code Block을 피해야합니다

이상적인 상황은 one line 코드로 쓰는 것이지만 그렇게 되지 않을 경우도 존재합니다. 이럴 경우 code block을 통해 2라인을 사용하는것 보다는 별도의 함수로 제작하여 사용하는 것이 좋습니다. 아래 예제를 볼 경우 아래가 더욱 가독성측면에서 뛰어난 것을 알 수 있습니다.

Foo foo = parameter -> { String result = "Something " + parameter; 
    //many lines of code 
    return result; 
};



Foo foo = parameter -> buildString(parameter);
private String buildString(String parameter) {
    String result = "Something " + parameter;
    //many lines of code
    return result;
}

마무리

자바8을 잘 활용할 수 있다면 보다 간편한 코딩을 할 수 있게 될 수 있는 것을 알 수 있었습니다.

감사합니다.

참조

https://www.baeldung.com/java-8-lambda-expressions-tips

댓글