BACK/JAVA

[JAVA] 람다식(메서드 구현 축약, 메서드 참조, 생성자 참조)

연듀 2022. 10. 2. 11:05

람다식이란?

 

기존의 객체 지향 프로그램 체계 안에서 함수형 프로그래밍을 가능하게 하는 기법

단 하나의 추상 메서드만을 포함하는 인터페이스를 함수형 인터페이스라 하고,

이 함수형 인터페이스의 호출 및 기능을 구현하는 방법을 새롭게 정의한 문법이다.

익명 이너 클래스의 축약된 형태라고 볼 수 있고, 단 하나의 추상 메서드만을 가진 함수형 인터페이스만 람다식으로 표현할 수 있다. 

 

 

 

1.구현 메서드의 약식 표현

 

 

인터페이스 구현 메서드 -> 람다식 변환

(입력매개변수) -> {
	// 메서드 내용
}

 

 

함수형 인터페이스의 객체를 생성하기 위한 람다식 표현 방법

interface A{
    void method1();
}

interface B{
    void method2(int a);
}

interface C{
    int method3();
}

interface D{
    double method4(int a, double b);
}
public class Main{
    public static void main(String[] args) {
        A a1 = new A(){ // 익명 이너 클래스 방식
            @Override
            public void method1(){
                System.out.println("입력 x 리턴 x 함수");
            }
        };
        // 람다식 표현
        A a2 = () -> {
            System.out.println("입력  x 리턴 x 함수");
        };
        A a3 = () -> System.out.println("입력 x 리턴x 함수");

		// 익명 이너 클래스 방식
        B b1 = new B(){
            @Override
            public void method2(int a){
                System.out.println("입력 o, 리턴 x 함수");
            }
        };
        // 람다식 표현 
        B b2 = (int a) -> {
            System.out.println("입력 o, 리턴 x 함수");
        };
        B b3 = (a) -> {
            System.out.println("입력 o, 리턴 x 함수");
        };
        B b4 = (a) -> System.out.println("입력 o, 리턴 x 함수");

        B b5 = a -> System.out.println("입력 o 리턴 x 함수");

		// 익명 이너 클래스 방식
        C c1 = new C(){
            @Override
            public int method3(){
                return 4;
            }
        };
		// 람다식 표현
        C c2 = () -> {return 4;};
        C c3 = () -> 4;
		
        // 익명 이너 클래스 방식
        D d1 = new D(){
            @Override
            public double method4(int a, double b){
                return a+b;
            }
        };
		// 람다식 표현
        D d2 = (int a, double b) -> {return a+b;};
        D d3 = (a, b) -> {return a+b;};
        D d4 = (a, b) -> a+b;
    }
}

 

 

 

2.메서드 참조

 

정의돼 있는 인스턴스 메서드 참조

클래스 객체::인스턴스 메서드명

 

interface A{
    void abc();
}

class B{
    void bcd(){
        System.out.println("메서드");
    }
}
public class Main{
    public static void main(String[] args) {
        
        // 익명 이너 클래스
        A a1 = new A(){
            @Override
            public void abc(){
                B b = new B();
                b.bcd();
            }
        };
        
        // 람다식
        A a2 = () -> {
            B b = new B();
            b.bcd();
        };
        
        // 정의된 인스턴스 메서드 참조
        B b = new B();
        A a3 = b::bcd;
        
        a1.abc();
        a2.abc();
        a3.abc();
    }
}

 

익명 이너 클래스 및 람다식을 이용해 A인터페이스 객체를 생성한 것을 보면,

abc() 메서드를 호출하면 B 객체 멤버인 bcd를 호출하는 것이므로 결국 abc()는 bcd()와 동일한 셈이다.

이를 인스턴스 메서드 참조로 간단히 표현할 수 있다.

A인터페이스 내부의 abc() 메서드는 참조 변수 b객체 내부의 인스턴스 메서드 bcd()와 동일하다는 의미가 된다. 

 

 

 

자바가 제공하는 인스턴스 메서드 참조

A a = System.out::println;

 

인터페이스 A의 추상 메서드인 abc()는 System.out.println()을 참조하라는 의미.

System.out 자체가 객체이므로 객체를 따로 생성할 필요없이 위와 같이 작성한다. 

 

interface A{
    void abc(int k);
}

public class Main{
    public static void main(String[] args) {
        A a1 = new A(){
            @Override
            public void abc(int k) {
                System.out.println(k);
            }
        };

        A a2 = (int k) -> {
            System.out.println(k);
        };

        A a3 = System.out::println;
        a1.abc(3);
        a2.abc(3);
        a3.abc(3);
    }

}

 

 

정의돼 있는 정적 메소드 참조

클래스명::정적 메서드명

 

정적 메서드는 객체 생성 없이 클래스명으로 바로 사용할 수 있기 때문에 객체의 생성 없이 클래스명을 바로 사용한다.

 

interface A{
    void abc();
}

class B{
    static void bcd(){
        System.out.println("메서드");
    }
}

public class Main {
    public static void main(String[] args) {
        A a1 = new A(){ // 익명 이너 클래스
            @Override
            public void abc(){
                B.bcd();
            }
        };
        
        A a2 = () -> {B.bcd();}; // 람다식
        
        A a3 = B::bcd; // 정적 메소드 참조
        
        a1.abc();
        a2.abc();
        a3.abc();
    }
}

 

 

 

첫 번째 매개변수로 전달된 객체의 인스턴스 메서드 참조

클래스명::인스턴스 메서드명

 

 

interface A{
    void abc(B b, int k);
}
class B{
    void bcd(int k){
        System.out.println(k);
    }    
}

public class Main{
    public static void main(String[] args) {
        A a1 = new A(){ //익명 이너 클래스
          @Override
          public void abc(B b, int k){
              b.bcd(k);
          }  
        };
        
        A a2 = (B b, int k) -> {b.bcd(k);}; // 람다식
        
        A a3 = B::bcd; // 직접 정의한 인스턴스 메서드 참조
        
        a1.abc(new B(), 3);
        a2.abc(new B(), 3);
        a3.abc(new B(), 3);
    }
}

 

 

 

자바가 제공하는 인스턴스 메서드 참조

interface A{
    int abc(String str);
}

public class Main{
    public static void main(String[] args) {
        A a1 = new A(){
            @Override
            public int abc(String str){
                return str.length();
            }
        };
        
        A a2 = (String str) -> str.length();
        
        A a3 = String::length; // 추상 메서드 abc()는 String 클래스의 length() 메서드와 동일한 기능
        System.out.println(a1.abc("안녕"));
        System.out.println(a2.abc("안녕"));
        System.out.println(a2.abc("안녕"));
    }
}

 

구현된 메서드의 내부에서는 입력매개변수로 넘어온 String 객체의 length() 메서드를 호출한다.

 

 

 

 

3.생성자 참조

 

배열 생성자 참조

배열 타입::new

 

인터페이스에 포함된 추상 메서드의 구현 메서드가 new 자료형[]과 같이 배열 객체의 생성 기능만을 수행할 때

람다식의 배열 생성자 참조 방법을 사용할 수 있다.

 

 

interface A{
    int[] abc(int len);
}

public class Main{
    public static void main(String[] args) {
        A a1 = new A(){ // 익명 이너 클래스
            @Override
            public int[] abc(int len){
                return new int[len];      
            }
        };
        
        A a2 = (int len) ->{ // 람다식
            return new int[len];
        };
        
        A a3= int[]::new; // 배열의 생성자 참조
        
        int[] array1 = a1.abc(3);
        int[] array2 = a2.abc(3);
        int[] array3 = a3.abc(3);
    }
    
}

 

 

 

클래스 생성자 참조

클래스명::new

 

추상 메서드를 구현할 때 new 생성자()와 같이 객체만을 생성해 리턴하면 클래스 생성자 참조를 사용할 수 있다.

 

 

 

interface A{
    B abc(int k); // 인터페이스에 포함된 추상메서드의 매개변수에 따라 호출될 생성자가 결정
}

class B{
    B(){
        System.out.println("첫 번째 생성자");
    }
    B(int k){
        System.out.println("두 번째 생성자");
    }
}
public class Main{
    public static void main(String[] args) {
        A a1 = new A(){
            @Override
            public B abc(int k){
                return new B(k);
            }
        };

        A a2 = (int k) -> new B(k);

        A a3 = B::new;

        a1.abc(3);
        a2.abc(3);
        a3.abc(3);
    }
}
두 번째 생성자
두 번째 생성자
두 번째 생성자

 

클래스가 여러 생성자를 갖고 있을 때, 인터페이스에 포함된 추상 메서드의 매개변수에 따라 어떤 생성자를 호출할지가 결정된다.