Ezcho

[Java] 람다식표현 본문

카테고리 없음

[Java] 람다식표현

Ezcho 2022. 11. 30. 19:24

람다식이란?

 

람다식은 다른말로 익명 메소드라고도 한다.

 

인터페이스 중에서 메소드를 하나만 가지고 있는 인터페이스를 함수형 인터페이스라고 한다. 이러한 함수형 인터페이스의 메서드를 람다식을 이용해 축소시킬 수 있다.(코드의 간결화)

 

-> Runable 인터페이스의 run메서드가 함수형 인터페이스의 대표적인 예시이다

- Runnable을 이용한 쓰레드 만들기

기존

    public class LambdaExam1 {

        public static void main(String[] args) {
            new Thread(new Runnable(){public void run(){
                for(int i = 0; i < 10; i++){
                    System.out.println("hello");
                }
            }}).start();
        }   
    }

하지만 이런 Runnable 인터페이스를 통해 쓰레드를 사용하기 위해선 항상 객체로 선언해주어야 한다.

그래서 람다식에서는 아래와 같이 표현할 수 있다.

    public class LambdaExam1 {  
        public static void main(String[] args) {
            new Thread(()->{
                for(int i = 0; i < 10; i++){
                    System.out.println("hello");
                }
            }).start();
        }   
    }

 

() -> {for문} 을 통해 표현하였다.

 

- JavaVirtual Machine (JVM) 은 Thread생성자(new Thread) 를 보고 ()->{} 이 무엇인지 대상을 추론한다. 

- Thread생성자 api를 보면 Runnable인터페이스를 받아들이는 것을 알 수 있다. (for문)

 

- JVM은 Thread생성자가 Runnable인터페이스를 구현한 것이 와야 하는 것을 알게 되고 람다식을 Runnable을 구현하는 객체로 자동으로 만들어서 매개변수로 넣어준다.

 

 

람다식 문법사용

 

람다식은 () -> {} 과 같은 형태로 표현한다.

아래와 같은 코드를 보자

    public interface Compare{
        public int compareTo(int value1, int value2);
    }

value1과 value2를 비교하는 compare  인터페이스의 경우 메서드가 하나밖에 존재하지 않아 함수형 인터페이스 라고 할 수 있다.

우리는 이런 CompareTo메서드를 람다식으로 사용해 볼것이다.

    public class CompareExam {      
        public static void exec(Compara compara){
            int k = 10;
            int m = 20;
            int value = compara.compareTo(k, m);
            System.out.println(value);
        }
        public static void main(String[] args) {    
            exec((i, j)->{
                return i - j;
            }); }

    }

Compare 인터페이스를 이용하는 클래스 이다.

Compara 인터페이스를 받아들인 후, 해당 인터페이스를 이용하는 exec메소드 가 존재한다. 

compareTo메소드가 어떻게 구현되어 있느냐에 따라서 출력되는 값이 다르다.

 

그럼 이제 클래스 상에서 람다식을 이용해 코드를 간결하게 쓰는법을 알아보자.

 내부 클래스

클래스 안에 선언된 클래스, 즉 필드를 선언하는 위치에 있는 클래스로, 중첩 클래스 또는 인스턴스 클래스라고 부르기도 한다.

public class Inner {
    class Test{
        int val = 0;
        public void add(){
            ++val;
        }
    }

위와 같이 구성된다.

예시를 하나 살펴보면

import java.util.*;
public class CarExam{
    public static void main(String[] args){
        List<Car> cars = new ArrayList<>();
        cars.add( new Car("작은차",2,800,3) );
        cars.add( new Car("봉고차",12,1500,8) );
        cars.add( new Car("중간차",5,2200,0) );
        cars.add( new Car("비싼차",5,3500,1) );
        
        printCar(cars, new CheckCarForBigAndNotExpensive());
    }
    
    public static void printCar(List<Car> cars, CheckCar tester){
        for(Car car : cars){
            if (tester.test(car)) {
                System.out.println(car);
            }
        }
    }
    
    interface CheckCar{
        boolean test(Car car);
    }
    
    //내부클래스를 만들어서 사용합니다.
    static class CheckCarForBigAndNotExpensive implements CheckCar{
        public boolean test(Car car){
            return car.capacity >= 4 && car.price < 2500;
        }
    }
}

위와 같은 클래스를 사용하였다. 내부클래스의 test 메서드는 capacity가 4보다 크고 price가 2500보다 작은 car객체를 소환할것이다.'

호출시에는 외부클래스.내부클래스() 식으로 사용한다.

 

익명클래스를 통해 더 짧게 사용해보자. 

익명 클래스

같은 검색조건에 대해 익명 클래스를 이용하면 별도 클래스를 만들 필요가 없으므로 코드가 조금 더 짧아집니다.

import java.util.*;
public class CarExam{
    public static void main(String[] args){
        List<Car> cars = new ArrayList<>();
        cars.add( new Car("작은차",2,800,3) );
        cars.add( new Car("봉고차",12,1500,8) );
        cars.add( new Car("중간차",5,2200,0) );
        cars.add( new Car("비싼차",5,3500,1) );
        
        printCar(cars, 
            //인터페이스 CheckCar를 구현하는 익명클래스를 만듭니다.
            new CheckCar(){
                public boolean test(Car car){
                    return car.capacity >= 4 && car.price < 2500;
                }
            });
    }
    
    public static void printCar(List<Car> cars, CheckCar tester){
        for(Car car : cars){
            if (tester.test(car)) {
                System.out.println(car);
            }
        }
    }
    
    interface CheckCar{
        boolean test(Car car);
    }  
}

 

람다식 표현

람다식을 이용하면 코드가 더욱더 간결해진다.

import java.util.*;
public class CarExam{
    public static void main(String[] args){
        //Car객체를 만들어서 cars에 넣습니다.
        List<Car> cars = new ArrayList<>();
        cars.add( new Car("작은차",2,800,3) );
        cars.add( new Car("봉고차",12,1500,8) );
        cars.add( new Car("중간차",5,2200,0) );
        cars.add( new Car("비싼차",5,3500,1) );
        
        CarExam carExam = new CarExam();
        carExam.printCar(cars, 
            //인터페이스 CheckCar의 test메소드에 대응하는 람다를 만듭니다.
            (Car car) -> { return car.capacity >= 4 && car.price < 2500; }
        );
    }
    
    public void printCar(List<Car> cars, CheckCar tester){
        for(Car car : cars){
            if (tester.test(car)) {
                System.out.println(car);
            }
        }
    }
    
    interface CheckCar{
        boolean test(Car car);
    }  
}

 

(Car car) -> { return car.capacity >= 4 && car.price < 2500; } 라는 람다를 만들어,

 

public boolean test(Car car){
	return car.capacity >= 4 && car.price < 2500;
}

위와 같이 존재했던 코드를 축소 시킬 수 있다.

 

 

 

Comments