자바8 람다식: 자바(Java): 람다식 (Lambda Expression) 기초 (Java 8 이상)

 

콜론 두개 (:: – 이중 콜론 연산자)의 정식 명칭은 메소드 참조 표현식(method reference expression)이며, 결론부터 말하자면 람다식에서 파라미터를 중복해서 쓰기 싫을 때 사용합니다.

말 그대로 람다 표현식(expression)에서만 사용 가능하고, 사용 방법은 [인스턴스]::[메소드명(또는 new)]으로 사용하는데, 예제를 통해 보는 것이 이해가 빠릅니다. 스태틱 메소드인 경우 인스턴스 대신 클래스 이름으로 사용할 수 있습니다.

 

예1) 리스트를 순회하면서 println을 하고자 할 때
import java.util.Arrays;
import java.util.List;

public class DoubleColonTest {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("김갑순", "김갑돌");

        // x를 건네고 받는 과정에서 x를 두 번 적게 된다.
        names.forEach(x -> System.out.println(x));

        // 아예 x들을 빼버리고 아래와 같이 작성할 수 있다.
        names.forEach(System.out::println);
    }
}

forEach의 첫 번째 구문은 람다식이 x를 파라미터로 넘기고 println(x)이 그 파라미터를 받는 과정에서 x를 두 번 사용하게 됩니다. 람다식이 건네는 파라미터와 받는 쪽의 파라미터가 동일할 때, 두 번째 구문처럼 System.out::println으로 줄여쓸 수 있습니다.

사용 방법이 [인스턴스]::[메소드명(또는 new)] 라고 했는데, 여기서는 System.out이 인스턴스 부분이며, 그 인스턴스의 메소드 중 하나인 println이 메소드명으로 사용되었습니다.

참고로 System.out은 PrintStream 인스턴스를 반환합니다.(API 문서)

 

예2) Stream의 map()을 사용해 새로운 스트림을 생성하고자 할 때
import java.util.Arrays;
import java.util.List;

public class DoubleColonTest {

    public String addNim(String s) {
        return s + "님";
    }

    public static void main(String[] args) {
        List<String> names = Arrays.asList("김갑순", "김갑돌");;

        DoubleColonTest dct = new DoubleColonTest();

        names.stream().map(x -> dct.addNim(x)).forEach(System.out::println); // 적용 전
        names.stream().map(dct::addNim).forEach(System.out::println); // 적용 후

    }
}

x -> dct.addNim(x)dct:addNim로 바꿀 수 있습니다.

만약 addNim()이 스태틱 메소드인 경우 다음과 같이 사용 가능합니다.

names.stream().map(DoubleColonTest::addNim).forEach(System.out::println);

 

예3) 생성자가 파라미터 한 개로 이루어진 DTO의 배열을 생성하고자 할 때
public class Dog {
    private String name;
    private String species;

    // ...setter
    // ...getter

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", species='" + species + '\'' +
                '}';
    }
}
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class DoubleColonTest {

    public static String addNim(String s) {
        return s + "님";
    }

    public static void main(String[] args) {
        List<String> names = Arrays.asList("김갑순", "김갑돌");

        List<Dog> dogs1 = names.stream()
                .map(x -> new Dog(x)) // 적용 전
                .collect(Collectors.toList());
        List<Dog> dogs2 = names.stream()
                .map(Dog::new) // 적용 후
                .collect(Collectors.toList());

        dogs2.forEach(x -> x.setSpecies("이탈리안 그레이 하운드"));

        System.out.println(dogs1);
        System.out.println(dogs2);

    }
}

x -> new Dog(x); 도 위와 같이 Dog::new 로 축약할 수 있습니다. 이것은 생성자 참조 표현식이라고도 합니다.

 

게터를 참조 표현식으로 변환: 스트림을 해시맵으로 그룹화하는 예제

... .stream()
                .collect(Collectors.groupingBy(Dog::getSpecies, HashMap::new, toList()));

// x -> x.getSpecies() 가 Dog::getSpecies 와 동일

x -> x.getSpecies()라는 람다식이 필요한 경우 Dog::getSpecies로 줄여쓸 수 있습니다.

 

예4) 함수형 인터페이스를 구현할 때 파라미터의 종류와 개수가 같으면 사용 가능 
@FunctionalInterface
public interface StringToDog {
    public String convert(String name, String species, int price);
}
public class Dog {
    private String name;
    private String species;
    private int price;

    public static String introduce(String name, String species, int price) {
        return name + " : " + species + " : " + price;
    }

    // ............
}
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class DoubleColonTest {

    public static void main(String[] args) {

        // 파라미터의 개수와 타입이 같다면 메소드 참고 표현식이 적용된다.
        StringToDog stringToDog1 = (name, species, price) -> Dog.introduce(name, species, price);
        StringToDog stringToDog2 = Dog::introduce;

        System.out.println(stringToDog1.convert("개똥이", "믹스", 100));
        System.out.println(stringToDog2.convert("누렁이", "믹스", 1000));
    }
}

함수형 인터페이스를 구현할 때 람다식을 사용하는데 파라미터의 개수, 종류와 표현식에서 사용된 메소드의 파라미터 개수, 종류가 같다면 위와 같이 축약할 수 있습니다.