본문 바로가기
자바/이것이 자바다

16. 람다식

by 989898 2023. 11. 20.

16.1 람다식이란?

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

데이터 처리부는 제공된 함수의 입력값으로 데이터를 넣고 함수에 정의된 처리 내용을 실행한다.

 

동일한 데이터라도 함수A를 제공해서 처리하는 결과와 함수B를 제공해서 처리하는 결과는 다를 수 있다.

 

이것이 함수형 프로그래밍의 특징으로, 데이터 처리의 다형성이라고도 볼 수 있다.

 

자바는 함수형 프로그래밍을 위해 Java 8부터 람다식을 지원한다.

 

람다식은 위 그림과 같이 데이터 처리부에 제공되는 함수 역할을 하는 매개변수를 가진 중괄호 블록이다.

 

데이터 처리부는 람다식을 받아 매개변수에 데이터를 대입하고 중괄호를 실행시켜 처리한다.

자바는 람다식을 익명 구현 객체로 변환한다. 익명 구현 객체란 9장에서 설명한 것과 같이 이름이 없는 인터페이스 구현 객체를 말한다. 예를 들어 다음과 같이 Calculable 인터페이스가 있다고 가정해보자.

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

이것을 람다식으로 표현하면 다음과 같다. 추상 메소드인 calculate()는 두 개의 매개변수를 가지므로 (x,y)로 표현되었고, 화살표 -> 뒤에 calculate()의 실행 블록이 온다.

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

action() 메소드를 호출할 때 매개값으로 다음과 같이 람다식을 제공할 수 있다. action() 메소드에서 calculable.calculate(x,y)를 실행하면 람다식의 중괄호 블록이 실행되면서 데이터가 처리된다. 여기서 action() 메소드는 제공된 람다식을 이용해서 내부 데이터를 처리하는 처리부 역할을 한다.

다음 그림과 같이 람다식1과 람다식2 중에서 어떤 람다식을 매개값으로 제공하느냐에 따라 계산 결과는 달라질 수 있다.

인터페이스의 익명 구현 객체를 람다식으로 표현하려면 인터페이스가 단 하나의 추상 메소드만 가져야 한다.

 

따라서 다음과 같이 두 개 이상의 추상 메소드를 가진 RemoteControl 인터페이스는 람다식으로 표현할 수 없다.

인터페이스가 단 하나의 추상 메소드를 가질 때, 이를 함수형 인터페이스라고 한다.

 

다음 인터페이스들은 함수형 인터페이스다.

인터페이스가 함수형 인터페이스임을 보장하기 위해서는 @FunctionalInterface 어노테이션을 붙이면 된다.

@FunctionalInterface를 붙이는 것은 선택사항이지만, 컴파일 과정에서 추상 메소드가 하나인지 검사하기 때문에 정확한 함수형 인터페이스를 작성할 수 있게 도와주는 역할을 한다.

 

Calculabe 인터페이스를 다음과 같이 작성해보자.

package ch16.sec01;

@FunctionalInterface
public interface Calculate {
	// 추상 메소드
	void caculate(int x, int y);
}

 

package ch16.sec01;

public class LambdaExample {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		action((x, y) -> {
			int result = x + y;
			System.out.println("result: " + result);
		});
		// action에 구현객체를 바로 집어 넣음. caculate를 구현한 객체임.
		// 외부에서 데이터 처리부에 함수를 제공해준다.
		// 람다식은 인터페이스의 익명 구현 객체이다.
		// 중괄호안 내용은 추상 메소드를 구현한 내용임.

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

	public static void action(Calculate caculable) {
		// 메서드는 데이터 처리부 -> 데이터 처리부는 데이터만 가지고 있을 뿐,
		// 처리 방법이 정해져 있지 않아 외부에서 제공된 함수에 의존한다.
		// action에 객체 입력시 중괄호에 있는 내용 실행.
		int x = 10;
		int y = 4;
		// 데이터 처리 -> 입력받은 구현 객체가 구현한 메서드 실행
		caculable.caculate(x, y);
	}

}

 


16.2 매개변수가 없는 람다식

함수형 인터페이스의 추상 메소드에 매개변수가 없을 경우 람다식은 다음과 같이 작성할 수 있다. 실행문이 두 개 이상일 경우에는 중괄호를 생략할 수 없고, 하나일 경우에만 생략할 수 있다.

Workable 인터페이스에 매개변수가 없는 work() 추상 메소드를 다음과 같이 작성해보자.

package ch16.sec02.exam01;

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) {
		// TODO Auto-generated method stub
		Person person = new Person();

		person.action(() -> {
			System.out.println("출근을 합니다.");
			System.out.println("프로그래밍을 합니다.");
		});
		// action 메서드 호출하면 구현한 객체를 실행한다.

		person.action(() -> System.out.println("퇴근을 합니다.")

		);
	}

}

다음은 9.6에서 실습한 익명 구현 객체의 예제를 수정한 것이다. 익명 구현 객체를 람다식으로 대체해 버튼의 클릭 이벤트를 처리하고 있다.

package ch16.sec02.exam02;

public class Button {
	// 정적 멤버 인터페이스, 함수형 인터페이스
	public static interface ClickListener {
		// 추상 메소드
		void onClick();
	}

	// 필드
	private ClickListener clickListener;

	// 메서드
	public void setClickListener(ClickListener clickListener) {
		this.clickListener = clickListener;
	}

	// 구현 객체 실행 메서드
	public void click() {
		this.clickListener.onClick();
	}
}

 

package ch16.sec02.exam02;

public class ButtonExample {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Button btnOk = new Button();

		btnOk.setClickListener(() -> {
			System.out.println("Ok 버튼을 클릭했습니다.");
		});

		btnOk.click();

		Button btnCancel = new Button();

		btnCancel.setClickListener(() -> {
			System.out.println("Cancel 버튼을 클릭했습니다.");
		}); // 객체 입력

		btnCancel.click();
	}

}


16.3 매개변수가 있는 람다식

함수형 인터페이스의 추상 메소드에 매개변수가 있을 경우 람다식은 다음과 같이 작성할 수 있다. 매개변수를 선언할 때 타입은 생략할 수 있고, 구체적인 타입 대신에 var를 사용할 수도 있다. 하지만 타입을 생략하고 작성하는 것이 일반적이다.

매개변수가 하나일 경우에는 괄호를 생략할 수도 있다. 이때는 타입 또는 var를 붙일 수 없다.

Workable 인터페이스에 매개변수가 두 개 있는 work() 추상 메소드를 다음과 같이 작성해보자.

package ch16.sec03;

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

 

그리고 Speakable 인터페이스에 매개변수가 한 개 있는 speak() 추상 메소드를 다음과 같이 작성한다.

package ch16.sec03;

@FunctionalInterface
public interface Speakable {
	void speak(String content);
}

 

 

Person 클래스에 Workable을 매개변수로 갖는 action1() 메소드와 Speakable을 매개변수로 갖는 action2() 메소드를 다음과 같이 작성한다.

package ch16.sec03;

public class Person {
	public void action1(Workable workable) {
		workable.work("홍길동", "프로그래밍");
	}

	public void action2(Speakable speakable) {
		speakable.speak("안녕하세요");
	}
}

 

이제 LamdaExample 클래스를 다음과 같이 작성하고 실행해보자.

package ch16.sec03;

public class LambdaExample {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Person person = new Person();

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

		person.action1((name, job) -> System.out.println(name + "이" + job + "을 하지 않습니다."));

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

	}
}

 


16.4 리턴값이 있는 람다식

함수형 인터페이스의 추상 메소드에 리턴값이 있을 경우 람다식은 다음과 같이 작성할 수 있다. return 문 하나만 있을 경우에는 중괄호와 함께 return 키워드를 생략할 수 있다. 리턴값은 연산식 또는 리턴값 있는 메소드 호출로 대체할 수 있다.

다음 예제를 통해 두 수를 계산하고 리턴하는 람다식을 연습해보자. Calculable 인터페이스에 리턴값이 있는 calc() 추상 메소드를 다음과 같이 작성한다.

package ch16.sec04;

public interface Calculate {
	double calc(double x, double y);
}

 

package ch16.sec04;

public class Person {
	public void action(Calculate calculate) {
		double result = calculate.calc(10, 4);
		System.out.println("결과" + result);
	}
}

 

package ch16.sec04;

public class LamdaExample {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Person person = new Person();

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

		person.action((x, y) -> x + y);

		person.action((x, y) -> sum(x, y));

	}

	public static double sum(double x, double y) {
		return x + y;
	}

}


16.5 메소드 참조

'자바 > 이것이 자바다' 카테고리의 다른 글

18  (0) 2024.11.25
17. 스트림 요소 처리  (0) 2023.11.21
15. 컬렉션 자료구조  (0) 2023.11.18
14. 멀티 스레드  (1) 2023.11.17
13. 제네릭  (1) 2023.11.17