반응형
JAVA8에 새롭게 추가된 것을 고르자면 람다를 고를 수 있다. 무조건 람다식을 공부해야 하는것이 아닌 람다의 근간이 되는 함수형 프로그래밍을 공부하여야 한다. JAVA에서 함수형 프로그래밍의 개념을 찾아볼 수 있는것이 바로 람다식이라고 한다.

그럼 함수형프로그래밍이란 무엇인지부터 살펴보자.

1. 함수형 프로그래밍이란?

먼저 이야기 하기 전에 다음과 같은 코드를 보자

take(10, print(number))

take 함수의 두번째 매게변수로 함수가 넘어가고 있다. JAVA에서는 사실 컴파일에러를 내뱉을 것이다. JAVA는 함수를 매개변수로 갖는것이 불가능 했다. 자바의 메소드는 일급 함수가 아니므로, 다른 메소드로 전달할 수 없다. 

일급함수(일급객체)의 특성을 간단히 정리한다면

1. 변수나 데이터 구조안에 담을 수 있다.
2. 파라미터로 전달할 수 있다.
3. 반환값으로 사용할 수 있다.
4. 할당에 사용된 이름과 상관없이 고유한 구별이 가능하다.

람다는 특정 클래스에 종속되지 않고 메서드처럼 파라미터리스트, 바디, 반환 형식등을 포함한다. 
Java8 에서 함수형 인터페이스(단 하나의 메소드만이 선언된 인터페이스)라는 개념을 도입하게 되었고, 함수형 인터페이스의 경우, 람다식으로 표현이 가능할 수 있게 제공하였다.

람다식의 기본적인 문법은 다음과 같다.

(arg1, arg2...) - > {body}
-> 을 기준으로 왼쪽은 파라미터 리스트 오른쪽은 행위이다. 

정리해보자면 아래와 같다.

  • 단순한 람다 구문의 경우, 람다 구분에 중괄호가 없을 수도 있다.

  • return 이 없을 수도 있다.

  • 매개변수에는 타입을 명시하지 않아도 된다. (타입 추론)

  • 람다식 문법을 컴파일러가 익명 클래스로 변환한다. 즉, 함수형 인터페이스를 컴파일러가 구현하도록 위임하는 형태라 볼 수 있다


단순히 문법만 짧아진것이 장점이 아니라 행위를 파라미터로 전달하므로서 더욱 유연하고 재사용 가능한 API를 만들 수 있게 되었다.

이제는 코드를 같이 보면서 람다에 대해 이야기 해보자.


람다 표현식은 구현해야될 추상 메서드가 1개인 인터페이스를 구현한 것이라고 앞서 이야기 하였다. 추후 유지보수를 할때 한개의 메소드를 추가하려하면 람다의 조건은 깨지므로 나머지 코드가 오류가 날것이다. 이를 방지하기 위한 어노테이션이 바로 @FunctionalInterface 이다.

/**
*해당 인터페이스가 함수형인터페이스라는것을 알려주고
*한개 초과의 메서드를 작성할 시 컴파일러 에러를 낸다.
*/
@FunctionalInterface
public interface Movable {
void move(String str);
}


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

2. 함수의 파라미터화
앞서 파라미터로 행위를 전달하는것이라고 했다.  자세한 내용을 다음 소스를 통해 알아보자.

public class Fruit {
private String name;
private String color;

Fruit(String name, String color){
this.name = name;
this.color = color;
}

String getName(){
return this.name;
}

String getColor(){
return this.color;
}
}




List<Fruit> extractApple(List<Fruit> fruits){
List<Fruit> resultList = new ArrayList<>();
        for(Fruit fruit : fruits){
            if("apple".equals(fruit.getName())){
             resultList.add(fruit);
        }
    }

         return resultList;
}

    List<Fruit> extractRed(List<Fruit> fruits){
    List<Fruit> resultList = new ArrayList<>();
         for(Fruit fruit : fruits){
            if("red".equals(fruit.getColor())){
             resultList.add(fruit);
        }
    }

    return resultList;
}


소스를 살펴보면 if문 외에는 전부 동일하다. 이걸 행위 파라미터를 이용해 1개의 메서드로 합치면 훨씬 보기가 좋을것 같다.

static List<Fruit> extractFruitList(List<Fruit> fruits, Predicate<Fruit> predicate){
    List<Fruit> resultList = new ArrayList<>();
    for(Fruit fruit : fruits){
        if(predicate.test(fruit)){
            resultList.add(fruit);
        }
    }

 

    return resultList;
}

호출해보자. 
List<Fruit> fruits = Arrays.asList(new Fruit("apple", "red"), new Fruit("melon", "green"), new Fruit("banana", "yellow"));

List<Fruit> appleList = extractFruitList(fruits, new Predicate<Fruit>() {
      @Override
      public boolean test(Fruit fruit) {
          return "apple".equals(fruit.getName());
      }
  });

List<Fruit> redList = extractFruitList(fruits, new Predicate<Fruit>() {
      @Override
      public boolean test(Fruit fruit) {
          return "red".equals(fruit.getColor());
      }
  });

Predicate 인터페이스는 추상메서드가 한개 뿐이므로 람다식 적용가능 하다. 각각에 람다를 적용한다면 다음과 같다.

List<Fruit> appleList = extractFruitList(fruits, fruit-> "apple".equals(fruit.getName()));
List<Fruit> redList = extractFruitList(fruits, fruit-> "red".equals(fruit.getColor()));

https://skyoo2003.github.io/post/2016/11/09/java8-lambda-expression


반응형
반응형

JDK 8 API 에서 파일에 대해 함수형 스트림을 사용할 수 있는 메서드가 추가되었다. 
- find를 이용해서 어떤 디렉토리에 존재하는 모든 파일을 가져오도록 하는 함수


Path start = Paths.get("");
int maxDepth = 5; // Integer.MAX_VALUE로 하면 모든 파일을 가져올 수 있다
try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) ->
        String.valueOf(path).endsWith(".js"))) {
    String joined = stream
        .sorted()
        .map(String::valueOf)
        .collect(Collectors.joining("; "));
    System.out.println("Found: " + joined);
}


 위 예제에선 현재 작업 디렉터리 내의 모든 파일을 훑는다. 그리고 각각의 경로를 각자의 문자 표현식(valueOf 메서드)으로 맵핑한다. 그 결과는 필터되고, 정렬되고 마지막으로 합쳐진(joined) 하나의 문자열이다. 만약 함수형 스트림에 익숙하지 않다면 Java 8 Stream Tutorial 을 읽어보길 권한다.

  find 메서드는 인자 3개를 받는다. 첫째는 start는 시작 경로이며 둘째는 maxDepth로 말 그대로 탐색할 최대 폴더 깊이를 의미한다. 마지막 세 번째는 탐색 로직을 결정하는 람다식이 들어가며 타입은 predicate이다. 위 예에서 모든 .js로 끝나는 javascript파일을 찾았다.

try/with 문장으로 감싸져 있다는 것을 깨달았을 것이다. Stream들은 AutoCloseable를 구현하고 있으며 이 경우 반드시 명시적으로 스트림을 닫아 주어야만 한다. 이는 뒷쪽에서 IO 연산이 수행되기 때문이다.


http://dextto.tistory.com/215



반응형

'개발공부 > JAVA8' 카테고리의 다른 글

JAVA8 - 람다  (0) 2018.07.18

+ Recent posts