SMALL

Java7에서 Java8으로 넘어가면서, 대표적인 변경 사항은 람다 표현식의 추가를  말할 수 있다.

 

  • 람다 표현식의 정의
  • 왜 추가되었는지
  • 람다 표현식의 특징
    • 함수형 인터페이스
    • 상태가 없는 객체 ( StateLess Object ) 
      • 메서드 vs 함수
    • 행위 파라미터화 ( Behavior Parameterize )

 


람다 표현식의 정의

Lambda Expression은 Anonymous Function이다. 

즉, "익명 함수" 이름이 없는 함수 

풀어서 말하면 식별자 없이 실행 가능한 함수 표현식이라고 할 수 있다.


왜 추가되었는지

Functional Programming Style을 Java에 적용하기 위해서 

Java는 대표적인 객체 지향 언어이다. 하지만 Lambda Expression은 함수 지향 언어에 가깝다.

( 객체 지향 언어에 functional programming style을 적용할 수 있으니 언어의 표현력(?)이 대폭 증가했다고 할 수 있다)

Lambda Expression의 장점은 아래와 같다 

  • 행위 ( 함수 ) 를 파라미터화 할 수 있다 (아래의 람다 표현식의 특징에서 부연 설명)
  • 코드를 간결하게 만들어 가독성을 향상 시킬 수 있다
  • 일반적으로 다중 CPU를 활용하는 형태로 구현되어 병렬 처리에 유리

다 표현식의 특징

람다 표현식의 특성 중 하나는 메서드의 인수로 전달될 수 있고, 변수로 저장될 수 있다는 점입니다.
기본적인 표현의 구성은 아래와 같습니다.

( Persion p1, Persion p2 ) -> p1.getAge().compareTo(p2.getAge())
  • Parameter list: (Persion p1, Persion p2)
  • 화살표: 람다의 파라미터와 바디를 구분
  • Lambda body: 람다의 반환값에 해당하는 표현식
    • (parameters) -> expression
    • (parameters) -> { statements; }

- 함수형 인터페이스

람다 표현식은 구현해야될 추상 메서드가 1개인 인터페이스를 구현한 것이다. 

인터페이스를 구현하고자하는데 어차피 구현해야 될 메서드가 1개뿐이니 이름이고 뭐고 다 지워버린 것이다. 

그럼 메서드가 2개일땐 어떻게 해야 할까? 람다로는 지원하지 않는다. 

람다 표현식으로 구현이 가능한 인터페이스는 오직 추상 메서드가 1개뿐인 인터페이스만 가능하며 그렇기 때문에 추상 메서드가 1개인 인터페이스를 부르는 명칭이 추가됐다. 그것이 함수형 인터페이스다.

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

- 상태가 없는 객체(Stateless Object)

익명객체를 구현할 때는 인스턴스 필드를 추가할 수 있다. 

하지만 람다는 바로 메서드를 구현해버리니 인스턴스 필드가 들어갈 공간이 없어 보인다. 

람다 표현식에 인스턴스 필드를 추가하려면 어떻게 해야할까? 방법은 없다.

 

메서드와 함수의 가장 큰 차이는 메서드는 객체에 종속되어있다는 것이다.

함수란 인풋(Input)에 의해서만 아웃풋(Output)이 달라져야 하는데

메서드는 객체에 종속적이기때문에 인풋이 달라지지 않아도 객체의 상태에 따라 결과값이 다를 수 있다.

함수형 프로그래밍에서 함수는 인풋에 의해서만 아웃풋이 달라져야하며 그것을 지원하기 위해 람다 표현식으로 구현할 때 객체는 상태를 가질 수 없다.

Thread th = new Thread(new Runnable(){
	int cnt = 99 ; // State 
    
	@Override
	public void run() {
    		System.out.println("new Thread!! ");
	}
});

- 행위 파라미터화(Behavior Parameterize)

보통 코드를 짤때 데이터를 매개변수로 전달하고 해당 데이터를 가지고 무언가 행위를 하는 메서드를 구현하기 마련이다. 

하지만 데이터를 전달하는 것이 아닌 행위를 전달하게 되면 좀 더 유연한 코드가 될 수 있다 ( 람다 표현식의 특징! )

    List<Product> filteredByName = filterByName(products, "새우깡");
    List<Product> filterByPrice = filterByPrice(products, 1000);

    public static List<Product> filterByName(List<Product> products, String name) {
        List<Product> filteredProducts = new ArrayList<>();
        for (Product product : products) {

            if (product.getName().equals(name)) { // filter ! 
                filteredProducts.add(product);
            }
        }

        return filteredProducts;
    }

    public static List<Product> filterByPrice(List<Product> products, int price) {
        List<Product> filteredProducts = new ArrayList<>();
        for (Product product : products) {

            if (product.getPrice() <= price) { // filter ! 
                filteredProducts.add(product);
            }
        }
        return filteredProducts;
    }

위의 코드는 일반적인 데이터를 매개변수로 전달하는 코드이다. 

 

public interface FilterPredicate {
    public abstract boolean filter(Product product);
}

List<Product> filteredByName011 = filter(products, p -> p.getName().equals("새우깡"));
List<Product> filterByPrice011 = filter(products,
    p -> p.getName().equals("새우깡") && p.getStore().equals("owner"));

public static ArrayList<Product> filter(ArrayList<Product> products, FilterPredicate filterInterface) {
    ArrayList<Product> filteredProducts = new ArrayList<>();

    for (Product product : products) {
        if (filterInterface.filter(product)) { // point ! 
            filteredProducts.add(product);
        }
    }
    return filteredProducts;
}

비교하는 행위를 파라미터로 입력받아 if문 내 조건을 하드코딩에서 동적으로 처리

 

 

 

 

참고 블로그 :)

 

LIST

+ Recent posts