본문으로 바로가기

Java 8 Lambda Expression - 람다식 #4

category 개발이야기/Java 2017. 9. 12. 23:15
반응형

Method Reference

Lambda의 마지막 입니다. 

기존 람다식을 더 줄일 수 있는 Method reference에 대해서 설명합니다.


기본 형식은 아래와 같습니다.

ClassName::Method

ex) Person::getAge

person class에 있는 getAge()함수의 레퍼런스를 표현으로 실제 실행하는것이 아니기 때문에 ()를 붙이지 않습니다.

아래 예제를 보면 람다식이 method reference로 어떻게 표현되는지 감을 잡을 수 있습니다.

(Person p) -> p.getAge()                     ==>    Person::getAge

() -> Thread.currentThread().dumpStack()  ==>   Thread.currentThread::dumpStack

(str, i) -> str.substring(i)                      ==>    String::substring

(String s) -> System.out.println(s)           ==>    System.out::println


Method reference를 만드는 방법

다음과 같이 세가지 형태에 대해서 method reference의 표기가 가능합니다.

1. class내부에 존재하는 static 함수인 경우

(args) -> ClassName.staticMethod(args)

ClassName::statckMethod

 ex) (String s) -> Integer.parseInt(s); /*Lambda expression*/

     Integer::parseInt; //method reference


2. class 내부에 존재하는 일반 함수인 경우

(arg0, rest) -> arg0.instanceMethod(rest);

arg0의ClassName::instanceMethod;

ex) (List<String> list, element) -> list.containts(element); /*Lambda expression*/

    List::contains; //method reference


3. 지역변수에 할당된 object가 있는경우

(args) -> expr.instnaceMethod(args)

expr::InstanceMethod

public class Main {

    public Person mPerson = new Person();


    public void methodReferenceTest() {

        Consumer<String> setName1 = name -> mPerson.setName(name);

        Consumer<String> setName2 = mPerson::setName; /*method reference*/


        Supplier<String> getName1 = () -> mPerson.getName();

        Supplier<String> getName2 = mPerson::getName; /*method reference*/

    }

}


class Person {

    private String mName;


    public String getName() {

        return mName;

    }


    public void setName(String name) {

        mName = name;

    }

}


생성자 레퍼런스

클래스를 생성 할 때 사용하는 생성자 역시 레퍼런스로 사용할 수 있습니다.

ClassName::new

위에 들었던 Person 객체처럼 만약 인수가 없는 생성자가 있다면 생성자의 Lambda signature는 () -> T 입니다. 

Supplier 형태 입니다. 

따라서 이런경우 아래와 같이 생성해서 사용할 수 있습니다.

Supplier<Person> constructor1 = Person::new;

Person p1 = constructor1.get();

만약 생성자에 인수가 하나 있다면 (T) -> R 형태이므로 Function을 사용하면 됩니다. 

따라서 Product의 class가 아래와 같다면 아래와 같이 표현할 수 있습니다.

Function<String, Product> constructor2 = Product::new;

Product p2 = constructor2.apply("새우깡");


만약 생성자가 여러개이거나 functionalInterface에서 지원하지 않는 형태일 경우 따로 정의해서 만들어 써도 됩니다. 


여기서 중요한 점은 생성자 reference를 사용할 경우 객체의 생성이 delay된다는 점입니다. 즉 lazy initialize가 가능하게 됩니다. 

실제 객체는 constructor1.get(); 처럼 get()을 호출하는 순간이나,  constructor2.apply("새우깡"); 을 호출하는 순간에 생성됩니다. 


이를 잘 이용한다면 factory method pattern에 아주 유용하게 쓸수 있습니다.

만약 A,B,C,D 타입에 따라 객체 생성을 해주는 클래스가 있다면 아래와 같이 구현됩니다.

public class Factory {

    public static final int A_TYPE = 0;

    public static final int B_TYPE = 1;

    public static final int C_TYPE = 2;

    public static final int D_TYPE = 3;


    public IType getNewClass(int type) {

        switch(type) {

            case A_TYPE:

                return new A();

            case B_TYPE:

                return new B();

            case C_TYPE:

                return new C();

            case D_TYPE:

                return new D();

            default:

                return new D();

        }

    }

}


interface IType {}


class A implements IType {}

class B implements IType {}

class C implements IType {}

class D implements IType {}

이 코드를 Method Reference를 사용시 아래와 같이 변경됩니다. 

Switch문 제거를 위해서 HashMap을 사용하면서 method reference로 value를 같습니다. 

이때의 ClassName::new는 정의된 시점이 아니라 get()함수가 불리는 순간 생성되므로 lazy initializing 되며, simple한 코드로 리팩토링 됩니다.

public class LambdaFactory {

    public static final int A_TYPE = 0;

    public static final int B_TYPE = 1;

    public static final int C_TYPE = 2;

    public static final int D_TYPE = 3;


    public static HashMap<Integer, Supplier<IType>> mTypeFactoryMap;

    static {

        mTypeFacotryMap = new HashMap<>();

        mTypeFacotryMap.put(A_TYPE, A::new);

        mTypeFacotryMap.put(A_TYPE, B::new);

        mTypeFacotryMap.put(A_TYPE, C::new);

        mTypeFacotryMap.put(A_TYPE, D::new);

    }


    public IType getNewClass(int type) {

        int key = D_TYPE;

        if (mTypeFacotryMap.containsKey(type)) {

            key = type;

        }

        return mTypeFacotryMap.get(key).get();

    }

}


반응형

'개발이야기 > Java' 카테고리의 다른 글

Java 8 Comparator  (0) 2017.09.14
Java 8 String join  (2) 2017.09.13
Java 8 Lambda Expression - 람다식 #3  (0) 2017.09.12
Java 8 Lambda Expression - 람다식 #2  (1) 2017.09.12
Java 8 Lambda Expression - 람다식 #1  (1) 2017.09.09