람다식

NOAH·2021년 1월 18일
0
post-thumbnail

람다식

람다식은(lamda expression)은 간단히 말해서

메서드를 하나의 식'(exepresseion)으로 표현 한 것입니다.

람다식은 함수를 간략하면서도 명확하게 표현할 수 있게 해줍니다.


int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random()*5) + 1);

위의 문장에서 '(i) -> (int)(Math.random()*5)+1' 이 바로 람다식이다.
이 람다식이 하는 일을 메서드로 표현하면 다음과 같습니다.

int method(int i){
	return (int)(Math.random()*5) + 1 ; 
}

메서드보다 람다식이 간결하면서도 이해하기 쉽다.

자바에서 모든 메서드는 클래스에포함되어야 하므로 클래스도 새로 만들고, 객체도 생성해야 비로소 이 메서드를 호출 할 수 있다.

그러나 람다식은 이 모든 과정없이 오직 람다식 자체만으로도 이 메서드의 역할을 대신할 수 있습니다.

람다식 작성하기

int max(int a, int b){
	return a > b ? a : b ; 
}

메서드를 람다식으로 만드는 방법은 간단합니다.

메서드에서 이름(max)과 반환타입(int)을 제거하고

매개변수 선언부와 몸통 사이에 '->' 만 추가하면 됩니다.

(int a, int b) -> {
	return a > b ? a : b ; 
}

반환값이 있다면,

return 대신에 'expression(식)' 으로 대신 할 수 있습니다.

식의 연산결과가 자동적으로 반환값이 됩니다.

이 떄는 문장(statement)가 아닌 식 이므로 끝에 ';'는 생략합니다.

(int a, int b) -> a > b ? a : b

람다식에 선언되 매개변수의 타입또한 추론이 가능한 경우 생략 가능합니다.
(대부분의 경우 그렇습니다.)

람다식에 반환 타입이 없는 이유도 항상 추론가능하기 때문입니다.

(a, b) -> a > b ? a : b

만약 선언된 매개변수가 하나 뿐인 경우 괄호도 생략 할 수 있습니다.
단, 매개 변수 타입이 있담녀 괄호를 생략할 수 없습니다.

(a) -> a * a 
(int a) -> a * a 

a -> a * a // OK
int a - > a * a // Error

마찬가지로 괄호{} 안의 문장이 하나 일 때는

(String name, int i) -> { System.out.println(name + "=" + i }

다음과 같이 괄호를 생략할 수 있습니다. 

```java
(String name, int i) -> System.out.println(name + "=" + i)


그러나 괄호안의 문장이 return문일 경우 괄호를 생략할 수 없습니다.

```java

(int a, int b) -> { return a > b ? a : b ;} // OK
(int a, int b) -> return a > b ? a : b // Error

람다식은 익명 함수가 아닌 '객체'입니다.

자바에서 모든 메서드는 클래스 내에 포함되어야 합니다.

하ㅁ다식은 어떤 클래스에 포함 되어 있을까요?

지금까지 람다식이 메서드와 동등한 것 같이 설명했지만

사실 람다식은 익명 클래스의 객체와 동등합니다.

예를 들어,

(int a, int b) -> a > b ? a : b

이 람다식은 메서드 이름을 max로 임의로 붙인 다음 객체와 동일하다고 볼 수 있습니다.

new Object() {
 int max(int a , int b){
 	return a > b ? a : b
 	}
}

호출 하려면 참조변수가 존재 해야 객체의 메서드를 호출 할 수 있으니

일단 이 익명의 객체 주소를 f 라는 참조 변수에 저장해봅니다.

// 참조변수의 타입은 무엇으로 해야 할까??? 
타입 f = (int a, int b) -> a > b ? a : b ;

참조변수 f 의 타입은 어떤 것이어야 할까요?

인터페이스나 클래스를 통해서 가능합니다.
그 중에서 인터페이스에 대해서 더 자세히 알아봅니다.

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

아래와 같이 매써드 max가 선언되 MyFunction인터페이스가 정의되어 있다고 해보면,

interface MyFunction {
 public abstract int max(int a, int b);
}

이 인터페이스를 구현한 익명 클래스의 객체는 다음과 같이 생성할 수 있습니다.


MyFunction f = new MyFunction(){
	public int max(int a, int b){
    		return a > b ? a : b ;
            }
};

int big = f.max(5, 3); // 익명객체의 메서드 호출

MyFuction 인터페이스에 정으된 메서드 max()는 람다식으로

'(int a, int b) -> a > b ? a : b '와 메서드의 선언부와 일치합니다.

따라서,

//익명 객체를 람다식으로 대체
MyFunction f = (int a, int b) -> a > b ? a :b ; 
//익명 객체의 메서드 호출
int big = f.max(5, 3);

이와 같이 인터페이스를 통해서 람다식을 다룬다면

기존의 자바의 규칙들을 어기지 않으면서도 자연스럽습니다.

람다식을 다루기 위한 인터페이스를 함수형 인터페이스 (Functional interface)라고 부릅니다. 람다식과 인터페이스의 메서드가 1:1로 연결 되기위해서 '오직 하나의 추상 메서드만' 정의되어 있어야한다는 제약이 있습니다.

@FunctionalInterface
interface Myfunction { // 함수형 인터페이스 MyFunction을 정의
 	public abstract int max(int a, int b);
  }

   

함수형 인터페이스 타입의 매개변수, 반환 타입

@FunctionalInterface
interface MyFunction {
	void myMethod(); // 추상메써드
}

메서드의 매개변수가 MyFunction타입이면, 이 메서드를 호출할 때 람다식을 참조하는 참조 변수를 매개변수로 지정해야 한다는 뜻 입니다.

void eMethod(MyFunction f) { //매개변수 타입 = 함수형 인터페이스
 f.myMethod();  // MyFunction에 정의된 메서드 호출
}

@FunctonalInterface
interface MyFunction {
 void run() ; // public abstract void run();
}

Class Ex14_1 {


	//매개변수 타입이 MyFunction인 메서드
	static void execute(MyFunction f){
    	f.run();
	}
    
    
    
    //반환 타입이 MyFunction인 메써드
    static MyFunction getMyFunction(){
    MyFunction f = () -> System.out.println("f3.run()");
    return f;
    }
    
    
    
    
    public static void main(String[] args){
    //람다식을 MyFunction의 run()을 구현
    MyFunction f1 = () -> System.out.println("f1.run()");
    //익명의 클래스로 run구현
    MyFunction f2 = new Myfunction() {
    //public 을 반드시 붙여야함
    public void run(){
    System.out.println("f2.run*()");
    }
    
    
    MyFunction f3 = getMyFunction();
    
    f1.run();
    f2.run();
    f3.run();
    execute(f1);
    execute( () -> System.out.println("run()") );
    
    }
}

--- output --- 

f1.run()
f2.run()
f3.run()
f1.run()
run()
--- --- --- --- 

Java.until.function 패키지

대부분 메서드는 타입이 비슷합니다.

매개변수가 0,1,2개
반환값은 0 또는 ₩개
게다가 지네릭 메서드로 저의하면 매개변수나 반환 타입이 달라도 문제가 되지 않습니다.

그래서 java.util.fuction패키지에
일반적으로 자주 쓰이는 형식의 메서드를 함수형 인터페이스로 미리 정해두었습니다.

매번 새로운 함수형 인터페이스를 정의하지 말고, 가능하면
이 패키지의 인터페이스를 활용하는 것이 좋습니다.

그래야 함수영 인터페이스에 정의된 메서드 이름도 통일되고,
재사용성이나 유지보수 측면에서도 좋습니다.


Predecate<T> Example.

Predicate<String> inEmptyStr = s -> s.length() == 0;
String s = "" ; 

if(inEmptyStr.test(s)) 
 System.out.println("This is am empty String");

매개변수가 두개인 함수형 인터페이스

매개변수 타입과 반환 타입이 일치하는 인터페이스

Predicate의 결합

여러 조건식을 논리 연산자인

0개의 댓글