이것이 자바다 14일차 - Chapter16 람다식

Seo-Faper·2023년 1월 27일
0

이것이 자바다

목록 보기
15/20

람다식이란

함수형 프로그래밍에서 나온 기법으로 함수를 정의하고 함수를 데이터 처리부로 보내 데이터를 처리하는 기법을 말한다. 데이터 처리부는 데이터만 가지고 있을 뿐, 처리 방법이 정해져 있지 않아 외부에서 제공된 함수에 의존한다.

람다식은 다음과 같이 쓸 수 있다.

(매개변수, ...) -> { 처리 내용 }

자바는 람다식을 익명 객체로 변환한다.
익명 객체란 이름이 없는 인터페이스 구현 객체를 말한다.

public interface Calculable {
	void calculate(int x, int y);
}

Calculable 인터페이스의 익명 구현 객체는 다음과 같이 생성할 수 있다.

new Calculable(){
	@Override
    public void calculate(int x, int y) { ... 처리 내용 }
}

그리고 이것을 람다식으로 표현하면 다음과 같다.

(x, y) -> { 처리 내용 }

람다식은 인터페이스의 익명 구현 객체이므로 인터페이스 타입의 매개변수에 대입될 수 있다.
예를 들어 다음과 같이 Calculable 매개변수를 가지고 있는 action() 메소드가 있다고 가정해보자.

public void action(Calculable calculable){
	int x = 10;
    int y = 4;
    calculable.calculate(x,y); //ㅔ이터를 제공하고 추상 메소드를 호출
}

action() 메소드를 호출할 때 매개값으로 다음과 같이 람다식을 제공할 수 있다.

action( (x,y) -> { 
	int result = x + y;
    System.out.println(result);
});

또한 인터페이스가 단 하나의 추상 메소드를 가지는 것을 함수형 인터페이스라고 한다.
그걸 람다식으로 줄여서 쓸 수 있는 것이다.

인터페이스가 함수형 인터페이스임을 보장하기 위해선 @FunctionalInterface 어노테이션을 붙이면 된다. 이걸 적으면 컴파일 과정에서 추상 메소드가 하나인지 검사하기 때문에 더욱 정확한 함수형 인터페이스를 작성할 수 있다.

매개변수가 없는 람다식

package ch16.sec02.exam01;

@FunctionalInterface
public interface Workable {
    void work();
}
package ch16.sec02.exam01;

public class Person {
    public void action(Workable workable){
        workable.work();
    }
}
package ch16.sec02.exam01;

public class LamdaExample {
    public static void main(String[] args) {
        Person person = new Person();

        person.action(()->{
            System.out.println("출근을 합니다.");
            System.out.println("프로그래밍을 합니다.");
        });

        person.action(()->System.out.println()); //실행문이 한개면 중괄호 생략 가능
    }
}

걍 이렇게 쓰면 된다.

매개변수가 있는 람다식

추상 메소드에 매개변수가 있을 시 람다식에서 매개변수 타입을 생략할 수 있고 구체적인 타입 대신에 var를 사용할 수도 있다.

(타입 매개변수, ...) ->{
	실행문;
    실행문;
}
(var 매개변수, ...) ->{
	실행문;
    실행문;
}
package ch16.sec03;

@FunctionalInterface
public interface Speakable {
    void speak(String content);
}
package ch16.sec03;

@FunctionalInterface
public interface Workable {
    void work(String name, String job);
}
package ch16.sec03;

public class Person {
    public void action1(Workable workable){
        workable.work("홍길동","프로그래밍");
    }
    public void action2(Speakable speakable){
        speakable.speak("안녕하세요.");
    }
}
package ch16.sec03;

public class LambdaExample {
    public static void main(String[] args) {
        Person person = new Person();

        person.action1((name, job) -> {
            System.out.println(name+"이");
            System.out.println(job+"을 합니다.");
        });

        person.action2(word->{
            System.out.println(word+"라고 말합니다.");
        });
    }
}

이렇게 매개변수를 람다식에 넣어 쓰면 된다.

리턴값이 있는 람다식

함수형 인터페이스의 추상 메소드에 리턴값이 있을 경우 람다식은 다음과 같이 작성할 수 있다.

(매개변수, ...) ->{
	실행문;
    return;
}

그리고 return문이 하나뿐일 경우 값 하나로 대채할 수 있다.

(매개변수, ...) ->
package ch16.sec04;

public class LambdaExample {
    public static void main(String[] args) {
        Person person = new Person();

        person.action((x, y) -> {
            double result = x + y;
            return result;
        });
        
        person.action((x, y) -> (x+y));
    }
}

이런식으로 쓸 수 있다.

메소드 참조

메소드 참조는 매개변수의 정보 및 리턴 타입을 알아내 람다식에서 불필요한 매개변수를 제거하는 것을 목적으로 한다.

예를 들어 두 개의 값을 받아 큰 수를 리턴하는 Math 클래스의 max() 정적 메소드를 호출하는 람다식은 다음과 같다.

(left,right) -> Math.left,right);

이 표현식을 람다의 메소드 참조를 이용해 표현하면 다음과 같다.

Math :: max;

기존의 쓰던 방식을 줄여서 쓸 수 있기 때문에 편리하다.

매개변수의 메소드 참조 역시 가능한데,

(a,b) -> { a.methodA(b); }

이것을 메소드 참조로 표현하면 다음과 같다. a의 클래스 이름 뒤에 :: 기호를 붙이고 메소드 이름을 기술한다. 작성 방법은 정적 메소드 참조와 동일하지만, a의 인스턴스 메소드가 사용된다는 점에서 다르다.

클래스 :: methodA

생성자 참조

생성자를 참조한다는 것은 객체를 생성하는 것을 의미한다. 람다식이 단순히 객체를 생성하고 리턴하도록 구성된다면 람다식을 생성자 참조로 대치할 수 있다. 다음 코드를 보면 람다식은 단순히 객체를 생성한 후 리턴만 한다.

(a,b) -> {return new 클래스(a,b);}

이것을 생성자 참조로 표현하면 다음과 같다. 클래스 이름 뒤에 :: 기호를 붙이고 new 연산자를 기술하면 된다.

클래스 :: new 
package ch16.sec05.exam03;

@FunctionalInterface
public interface Creatable1 {
    public Member create(String id);
}
package ch16.sec05.exam03;

@FunctionalInterface
public interface Creatable2 {
    public Member create(String id, String name);
}
package ch16.sec05.exam03;

public class Member {
    private String id;
    private String name;
    public Member(String id){
        this.id = id;
        System.out.println("Member(String id)");
    }

    public Member(String id, String name){
        this.id = id;
        this.name = name;
        System.out.println("Member(String id, String name)");
    }

    @Override
    public String toString() {
        String info = "{id: }"+id+", name: "+name+"}";
        return info;
    }
}
package ch16.sec05.exam03;

public class Person {
    public Member getMember1(Creatable1 creatable){
        String id = "winter";
        Member member = creatable.create(id);
        return member;
    }
    public Member getMember2(Creatable2 creatable){
        String id = "winter";
        String name = "한겨울";
        Member member = creatable.create(id,name);
        return member;
    }

}
package ch16.sec05.exam03;

public class ConstructorReferenceExample {
    public static void main(String[] args) {
        Person person =  new Person();

        Member m1 = person.getMember1(Member::new);
        System.out.println(m1);
        System.out.println();

        Member m2 = person.getMember2(Member::new);
        System.out.println(m2);
    }
}

이렇게 쓸 수 있다.

연습문제

4번 어노테이션은 참고만 하는 겁니다.

4번 매개변수 있어도 할 수 있습니다.


2번 2개 이상이면 ()로 묶어야합니다.

 Thread thread = new Thread(() -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("작업 스레드가 실행됩니다.");
            }
        });

(()-> System.out.println("Ok 버튼을 눌렀습니다.);})
(()-> System.out.println("Cancel 버튼을 눌렀습니다.);})

@FunctionalInterface
public interface Function{

    public double apply(double a, double b);
}


(x,y) -> Math.max(x,y)
(x,y) -> Math.min(x,y)


public static double avg(Function<Student> function){
	int sum = 0;
    for(Student i : students) sum += function.apply(student);
	return (double) sum / students.length;
}

Student::getEnglishScore
Student::getMathScore
profile
gotta go fast

0개의 댓글