람다 표현식이란?

2020-09-09

해당 Post는 람다 표현식에 대해서 정리한 파일입니다.


람다 표현식(Lamda Expression)이란

2014년에 발표된 자바의 버전인 Java SE 8 버전에서는 많은 사항이 변경되거나 새롭게 추가되었습니다.

그 중 람다 표현식은 간단히 말해 메소드를 하나의 식으로 표현한 것입니다.

즉, 식별자 없이 실행할 수 있는 함수 표현식을 의미하며, 따라서 익명 함수(anonymous function)라고도 부릅니다.

클래스를 만들고 객체를 생성하지 않아도 메소드를 사용할 수 있습니다.

또한, 람다 표현식은 메소드의 매개변수로 전달될 수도 있고, 메소드의 결괏값으로 반환될 수도 있습니다.

Java SE 8 버전부터는 람다 표현식을 사용하여 자바에서도 함수형 프로그래밍을 할 수 있게 되었습니다.

함수형 프로그래밍 : 함수형 프로그래밍은 순수 함수를 구현하고 호출함으로써 외부 자료에 부수적인 영향을 주지 않도록 구현하는 방식입니다.
순수 함수란 매개변수만을 사용하여 만드는 함수이다.
즉 함수 내부에서 함수 외부에 있는 변수를 사용하지 않아 함수가 수행되더라도 외부에 영향을 주지 않는다.

람다 표현식의 장,단점

장점 : 기존의 불필요한 코드를 줄여주고, 작성된 코드의 가독성을 높일 수 있습니다.

단점 : 디버깅하기 힘들어질 수 있습니다.

람다 표현식의 형식

(parameters) -> expression 또는 (parameters) -> { statements; }

여기에 몇가지 구분이 됩니다.

  1. 매개변수의 타입을 추론할 수 있는 경우에는 타입을 생략할 수 있습니다.

  2. 매개변수가 하나인 경우에는 괄호(())를 생략할 수 있습니다.

  3. 함수의 몸체가 하나의 명령문만으로 이루어진 경우에는 중괄호({})를 생략할 수 있습니다. (이때 세미콜론(;)은 붙이지 않음)

  4. 함수의 몸체가 하나의 return 문으로만 이루어진 경우에는 중괄호({})를 생략할 수 없습니다.

  5. return 문 대신 표현식을 사용할 수 있으며, 이때 반환값은 표현식의 결괏값이 됩니다. (이때 세미콜론(;)은 붙이지 않음)

간단한 예시를 봅시다.

int min(int x, int y) {
    return x < y ? x : y;
}

위의 예제 메소드를 람다 표현식으로 표현하면,

(x, y) -> x < y ? x : y;

이렇게 줄어드는 것을 확인할 수 있습니다.

그런데 자바에서는 클래스의 선언과 동시에 객체를 생성하므로, 단 하나의 객체만을 생성할 수 있는 클래스를 익명 클래스라고 합니다.

따라서 자바에서 람다 표현식은 익명 클래스와 같다고 할 수 있습니다.

지난 번에 보았던 섬 연결하기 문제의 코드를 꺼내봅시다.

import java.util.*;
class MyComparator implements Comparator<int[]>{
	@Override
	public int compare(int[] o1, int[] o2){
		return o1[2]-o2[2]; 
	}
}
class Solution {
    static int[] parent;
    public int solution(int n, int[][] costs) {
		int answer = 0;
		make(n);
    Arrays.sort(costs, new MyComparator());
    
		for( int[] cost : costs ) {	
			int from = cost[0];
			int to = cost[1];
			int weight = cost[2];
			
			if( isConnect(from,to) ) continue;
			else {
				answer+=weight;
				union(from,to);
			}
		}
		return answer;	
    }
    private static void make(int n)
    {
        parent = new int[n];
		for(int i=0; i<n; i++) {
			parent[i]=i;
		}
    }
    
    private static int Find(int index) {
		if(parent[index]==index) return index;
		return Find(parent[index]);
	}
    private static void union(int from, int to) {
		from = Find(from);
		to = Find(to);
		if( from < to ) parent[from] = to;
		else parent[to] = from;
	}
	private static boolean isConnect(int from, int to) {
		from = Find(from);
		to = Find(to);		
		return from==to;
	}
}

섬 연결하기 포스팅

이 중에서 윗 부분만 잠깐 봅시다.

import java.util.*;
class MyComparator implements Comparator<int[]>{
	@Override
	public int compare(int[] o1, int[] o2){
		return o1[2]-o2[2]; 
	}
}
class Solution {
    static int[] parent;
    public int solution(int n, int[][] costs) {
		int answer = 0;
		make(n);
    Arrays.sort(costs, new MyComparator());

이 부분은 Comparator를 사용하기 위해서 MyComparator를 만들어서 compare를 이용해 정렬 방식을 정해주는 형식이었습니다.

여기에 람다식을 적용한다면 아래와 같이 바뀌게 됩니다.

class Solution {
    static int[] parent;
    public int solution(int n, int[][] costs) {
		int answer = 0;
		make(n);
    Arrays.sort(costs,(o1,o2)->{
			return o1[2]-o2[2];
		});		

확실히 코드가 많이 줄어들었다는 것을 확인할 수 있습니다.

함수형 인터페이스(functional interface)

람다 표현식을 사용할 때는 람다 표현식을 저장하기 위한 참조 변수의 타입을 결정해야만 합니다.

자바에서는 참조 변수 없이 메서드를 호출할 수 없기 때문에 함수형 인터페이스를 만들고 인터페이스에 람다식으로 구현할 메서드를 선언합니다.

참조변수의타입 참조변수의이름 = 람다 표현식

위의 문법처럼 람다 표현식을 하나의 변수에 대입할 때 사용하는 참조 변수의 타입을 함수형 인터페이스라고 부릅니다.

함수형 인터페이스는 추상 클래스와는 달리 단 하나의 추상 메소드만을 가져야 합니다.

(인터페이스에 실수로 다른 메서드를 추가할 수 있는데, 이를 방지하기 위해서 @FunctionalInterface 어노테이션 사용)

위와 같은 어노테이션을 인터페이스의 선언 앞에 붙이면, 컴파일러는 해당 인터페이스를 함수형 인터페이스로 인식합니다.

자바 컴파일러는 이렇게 명시된 함수형 인터페이스에 두 개 이상의 메소드가 선언되면 오류를 발생시킵니다.

아래의 예시를 봅시다!

@FunctionalInterface
interface lambdaTest {	// 함수형 인터페이스의 선언
	public int sum(int x, int y);
}
public class Main2 {
	public static void main(String[] args){
		lambdaTest sumResult = (x, y) ->  x + y;	// 추상 메소드의 구현
		System.out.println(sumResult.sum(3, 4));	// 함수형 인터페이스의 사용 
	}
}

참고 사이트는 TCPSchool 입니다!

TCPschool Link

부족한 부분이 있다면 댓글 남겨주세요!