▸JAVA/기본 문법

람다식(Lamdba Expressions)

코데방 2019. 12. 23.
728x90

[ 람다식 문법 ]

  • 익명 클래스를 구현하는 것과 유사한 방식
  • 이전 방식에 비해 구현 코드를 간소화할 수 있음

람다식은 익명 클래스를 다루는 방식과 비슷합니다. 다만 문법을 조금 더 보기 편하게 간소화 시켜둔 것이라 볼 수 있습니다. 하지만 너무 남용할 경우 오히려 코드의 가시성이 떨어질 수 있으니 주의해야 합니다. 굳이 꼭 써야하는 문법은 아니며 사용에 대한 호불호도 많이 갈리는 문법입니다. 특히 객체지향적인 특성에서 많이 벗어나게되는 문법이라 싫어하는 사람들도 많은 것 같습니다. 다만 스트림 등의 특정 API에서는 람다식으로 작성해야 할 경우도 있습니다.

 


 

먼저 일반적인 익명 클래스 사용 예시입니다. 이전글에서 다룬 제네릭을 사용해서 내용이 없는 일종의 추상 메소드를 하나 만들고 사용 시에 바로 오버라이딩 해서 사용합니다.

 

package study.first;

public class Main {
	public static void main(String[] args) {

		
		Test t1 = new Test() { 
			
			<T>void myPrint(T a){
				
				System.out.println(a.toString());
			}
		};
		
		t1.myPrint("익명클래스");
		t1.myPrint(123);
		
	}
}

class Test {
	
	// 추상 클래스
	<T>void myPrint(T a) {
		
		
	}
}

 


 

기본적으로 람다식은 위의 방식을 간결하게 바꿔둔 것이라고 봐도 무방합니다. 익명 클래스는 여러 필드나 메소드로 이루어진 클래스에서 필요한만큼만 오버라이딩해서 사용하는 반면, 람다식은 딱 하나의 메소드를 가진 인터페이스를 구현하여 이를 오버라이딩해서 사용합니다. 인터페이스는 껍데기이므로 계속 재활용해서 오버라이딩 해도 무방합니다. 위 코드를 람다식으로  바꾸면 아래와 같이 사용할 수 있습니다. 

 

함수 이름을 굳이 써주지 않아도 되고 한줄짜리 코드는 중괄호도 필요 없어서 코드가 간결합니다. 람다식에서 작성하는 코드가 여러 줄이면 화살표 다음에 중괄호로 영역을 지정해주면 됩니다. 람다식 구현을 위한 인터페이스는 무조건 하나의 메소드만을 가져야만 하며, 이 때문에 함수형 인터페이스라고 부릅니다.

 

package study.first;

public class Main {
	public static void main(String[] args) {

		String a = "람다식";
		Test<String> t1 = (t) -> System.out.println(t);
		t1.myPrint(a);

		Integer b = 123;
		Test<Integer> t2 = (t) -> {
			System.out.print(t + " ");
			System.out.print(t + 10 + " ");
			System.out.print(t + 20 + " ");
		};
		t2.myPrint(b);
	}
}

// 람다식 구현을 위한 인터페이스(제네릭 선언)
interface Test<T> {
	void myPrint(T t);
}

 


 

당연한 말이겠지만 기존 함수의 사용과 같이 람다식 또한 인자값으로 제공할 수 있습니다. 함수의 리턴값을 바로 또 다른 함수의 인자값으로 넣는 것 처럼 람다식도 동일하게 사용 가능합니다. 하나의 인터페이스를 구현하는 객체지만 메소드를 딱 하나 가진 인터페이스이기 때문에 결국 C언어 함수와 동일하게 사용 가능한 함수라고 생각하면 됩니다. 

 

package study.first;

public class Main {
	public static void main(String[] args) {

		
		Test<String> t1 = (t) -> {
			
			String temp = "";
			
			for (int i = 0; i < t.length(); i++) {
				temp += t.charAt(i);
				temp += "-";
			}
			
			return temp;
		};

		
		String a = "람다식";
		System.out.println(t1.myPrint(a));

	}
}

// 람다식 구현을 위한 인터페이스(제네릭 선언)
interface Test<T> {
	String myPrint(T t);
}

 


 

[ 지역 변수 수정 불가 ]

  • 람다식 안에서 필드 값은 수정이 가능하지만 지역변수는 수정이 불가

문법상 람다식 안에서 람다식 밖의 지역변수는 final이 선언된 변수처럼 사용됩니다. 즉, 값을 가져와서 쓸 수는 있지만 바꿀 수는 없습니다. 람다식 안에서 생성된 지역변수는 당연히 수정이 가능합니다. 

package study.first;

public class Main {

	static int test1 = 1;

	public static void main(String[] args) {

		int test2 = 1;

		Test<String> t1 = (t) -> {

			test1 = 2; // 정상 (필드)
			test2 = 3; // 오류 (지역변수)
		};
	}
}

// 람다식 구현을 위한 인터페이스(제네릭 선언)
interface Test<T> {
	void myPrint(T t);
}

 


 

[ 람다식 메소드 참조 ]

  • 람다식 안에서 직접 코드를 사용하지 않고 이미 작성된 메소드를 참조하도록 할 수 있음
  • 메소드명에 관계 없이 리턴타입과 매개변수만 일치하면 참조 가능

직접 람다식 안에 코드를 짜지 않고 해당 클래스나 다른 클래스에서 작성된 메소드를 대입해서 사용하는 방식입니다.

package study.first;

public class Main {

	public static void main(String[] args) {

		// 인스턴스 메소드 참조
		Func f1 = new Func();
		Test t1 = f1::funcEx;
		t1.myPrint();
		
		// static 메소드 참조
		Test t2 = Func::funcEx2;
		t2.myPrint();
	}
}

// 람다식 구현을 위한 인터페이스(제네릭 선언)
interface Test {
	void myPrint();
}

// another Class
class Func {
	
	void funcEx() {
		
		System.out.println("인스턴스 메소드 참조");
	}
	
	static void funcEx2() {
		
		System.out.println("static 메소드 참조");
	}
}

 


 

[ 함수형 인터페이스 API ]

  • java.util.function 패키지에서 제공하는 기본 함수형 인터페이스
  • 구현할 람다식의 리턴값과 매개변수 타입이 맞다면 굳이 직접 생성하지 않고 가져다 사용하면 됨

기본 라이브러리에 포함된 함수형 인터페이스입니다. 굳이 인터페이스를 만들지 않고 편하게 가져다 사용할 수 있습니다. 

인터페이스 메소드 설명
Function<T, R> R apply(T) T 타입 인자 처리 후 R 타입 값 반환
Predicate<T> boolean test(T) T 타입 인자 처리 후 boolean 값 반환
Consumer<T> void accept(T) T 타입 인자 처리 (반환값 없음)
Supplier<T> T get() 인자 없이 처리 후 T 타입 값 반환

 

728x90

댓글

💲 추천 글