자바8에서는 메서드를 다른 메서드의 인수로 넘겨주는 기능을 제공한다 → 동작 파라미터화

 

동작파라미터화를 쓰면:

  1. 리스트의 모든 요소에 대해서 어떤 동작을 수행할 수 있음
  2. 리스트 관련 작업을 끝낸 다음에 어떤 다른 동작을 수행할 수 있음
  3. 에러가 발생하면 정해진 어떤 다른 동작을 수행할 수 있음

  코드가 간결해짐 

소프트웨어 공학의 원칙 DRY -don’t repeat yourself- (같은 것을 반복하지 말 것) 만족

 

람다:

메서드로 전달할 수 있는 익명 함수를 단순화한 것이라고 할 수 있다.

 

람다의 특징:

  1. 익명: 보통의 메서드와 달리 이름이 없으므로 익명이라 표현한다. 구현해야 할 코드에 대한 걱정거리가 줄어든다.
  2. 함수: 람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다. 하지만 메서드처럼 파라미터 리스트, 바디, 반환 형식, 가능한 예외 리스트를 포함한다.
  3. 전달: 람다 표현식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
  4. 간결성: 익명클래스처럼 많은 자질구레한 코드를 구현할 필요가 없다.

람다표현식은 마라미터, 화살표, 바디로 이루어진다.

(Apple a1, Apple a2) → a1.getWeight().compareTo(a2.getWeight());

람다파라미터                                  람다 바디

 

5가지 람다 표현식

(String s) → s.length() : String 형식의 파라미터 하나를 가지며 int를 반환한다. return은 함축되어있다.

(Apple a) → a.getWeight() > 150 : Apple 형식의 파라미터 하나를 가지며 boolean을 반환한다.

(int x, int y) → {

System.out.println(”Result” + (x + y) );

} : void 리턴. 이 예제에서 볼 수 있듯이 람다 표현식은 여러행의 문장을 포함할 수 있다.

() → 42 : 파라미터가 없으며 int 42를 반환한다.

(Apple a1, Apple a2) → a1.getWeight().compareTo(a2.getWeight()) : 파라미터 2개를 가지며 int를 반환한다.

 

자주쓰는 예제:

Collections.sort의 인수로 동작을 넘긴다. (커스텀 정렬)

public class LambdaSort {
    public static void main(String[] args) {


        String name = "KimKwangmin";
        String name2 = "ParkSoHyun";
        List<String> nameList = new ArrayList<String>();
        nameList.add(name);
        nameList.add(name2);
        Collections.sort(nameList, (a, b)-> Integer.compare(a.length(), b.length()));

        for (int i = 0; i < nameList.size(); i++){
            System.out.println(nameList.get(i));
        }

    }

}

Runnable을 정의할 때 등

public class LambdaRunnable {
    public static void main(String[] args) {
        Runnable r1 = () -> System.out.println("안녕하세요");
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                try{
                    //시가지연은 인터럽트를 발생할 수 있으므로 try-catch로 감싼다
                    Thread.sleep(2000);
                    System.out.println("김광민입니다");
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        };

process(r1);
process(r2);
process(()->System.out.println("잘 부탁드립니다."));

    }
    public static void process(Runnable r){
        r.run();
    }
}

 

그래서 어떤 순서로 코드를 짜야하는데?

  1. 동작 파라미터화를 기억하라
  2. 파일에서 한번에 한줄만 읽을 수 있는 코드가 있을때 기존의 설정, 정리 과정은 재사용하고 메서드만 다른 동작을 수행하도록 명령할 수 있다면 좋을 것이다. 람다를 이용해서 동작을 전달할 수 있다.
  3. 함수형 인터페이스를 이용해서 동작을 전달한다.
  4. 동작을 실행한다.
  5. 람다로 전달한다.

java.util.function 에서 제공하는 함수형 인터페이스들:

1.Predicate : test라는 추상 메서드를 정의하며 T객체를 받아 불리언을 반환한다.

public class PredicateTraining {
    public static void main(String[] args) {
        List<String> strs = new ArrayList<>();
        strs.add("하늘");
        strs.add("바다");
        strs.add("땅");

        List<String> resultList =filter(strs, (String s) -> s.length()==1);

        for (String s: resultList
             ) {
            System.out.println(s);
        }
    }

    public static  <T> List<T> filter(List<T> list, Predicate<T> p){
        List<T> result = new ArrayList<>();
        for(T t: list){
            if(p.test(t)){
                result.add(t);
            }
        }
        return result;
    }
}

2.Consumer : T형식의 객체를 인수로 받아서 void를 반환하는 accept라는 추상 메서드를 정의한다.

public class ConsumerTraining {
    public static void main(String[] args) {
prinAll(Arrays.asList("김광민", "박소현", "양성민", "권영우"), (String s)->{System.out.println(s);
            System.out.println("입니다");});
    }

    public static <T> void prinAll(List<T> list, Consumer<T> c){
        for(T t: list){
            c.accept(t);
        }
    }
}

3.Function: 제네릭 형식 T를 인수로 받아서 제네릭 형식 R객체를 반환하는 추상 메서드 apply를 정의한다.

public class FunctionTraining {
    public static void main(String[] args) {
        List<String> strs =convert(Arrays.asList(1,2,3,4,5), (Integer i)->i+"번");
        for(String s: strs){
            System.out.println(s);
        }
    }
    public static  <T, R> List<R> convert(List<T> list, Function<T, R> f){
        List<R> result = new ArrayList<>();
        for(T t: list){
            result.add(f.apply(t));
        }
        return result;
    }
}

 

더보기:

 

람다 표현식에서는 익명 함수가 하는 것처럼 자유 변수를 활용할 수있다. 이와 같은 동작을 람다 캡처링이라고 부른다. 자유 변수에는 약간의 제약이 있는데 지역 변수는 명시적으로 final로 선언되어 있어야 하거나 실질적으로 final로 선언된 변수와 똑같이 사용되어야 한다. 인스턴스 변수는 힙에 저장되는. 반면 지역 변수는 스택에 위치한다. 람다가 스레드에서 실행되면 변수를 할당한 스레드가 사라져서 변수 할당이 해제되었는데도 람다를 실행하는 스레드에서는 해당 변수에 접근하려 할 수 있기 때문이다.

'웹개발 > java' 카테고리의 다른 글

[자바8] 스트림이란?  (0) 2023.01.26
[자바8] 메서드 참조란?  (0) 2023.01.26
JAVA 프론트엔드와 CORS설정  (0) 2022.08.09
JAVA Optional / ifPresent  (0) 2022.08.02

+ Recent posts