람다식

김설영·2022년 4월 29일
0

함수형 언어

  • 자바는 "객체 지향 언어(OOP)" 이지만, 함수형 언어(FP)를 추가하였음.

  • FP 대표 : Haskell, Erlang, Scala

  • Python, js : OOP + FP

람다식

  • 함수(메서드)를 간단한 "식"으로 표현하는 방법

  • 익명 함수, 이름이 없는 함수 라고도 한다.

  • 함수 vs 메서드

    • 근본적으로 동일함. 함수는 일반적인 용어, 메서드는 객체 지향 개념 용어
    • 함수는 클래스에 대해 독립적이고, 메서드는 클래스에 종속적이다.
  • 작성법

    1. 메서드의 이름과 반환 타입을 제거하고, '->'를 블록{} 앞에 추가한다.
    2. 반환값이 있는 경우, 식이나 값만 적고 return문 생략 가능.
    3. 끝에 ';'를 붙이지 않는다.
    4. 매개변수 타입이 추론 가능하면 생략 가능 (대부분의 경우 생략 ok)
int max(int a, int b) {
	return a > b ? a : b;
}

// 람다식
(int a, int b) -> a > b ? a : b

// 매개변수 타입이 추론 가능하면 생략 가능 (대부분의 경우 생략 ok)
(a, b) -> a > b ? a : b
  • 주의사항
    1. 매개변수가 하나인 경우, 괄호 생략 가능 (타입이 없을 때만)
    2. 블록 안의 문장이 하나뿐일 때, 괄호{} 생략 가능 (끝에 ';' 안붙임)
    3. 단, 하나뿐인 문장이 return문인 경우 괄호{} 생략 불가
(a) -> a * a
a -> a * a 		// ok
int a -> a * a  // ERROR

(int i) -> { System.out.println(i) }
(int i) -> System.out.println(i)     // ok

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

int roll() {
	return (int) (Math.random() * 6); 
}
() -> (int) (Math.random() * 6);
  • 람다식은 익명 함수가 아니고, 익명 객체다!!!

  • 람다식(익명 객체)을 다루기위한 참조변수가 필요하다. 참조변수의 타입은 ??

// 원래는 익명 객체임
Object obj = new Object() {  // 익명 객체 : 객체의 선언과 생성을 동시에
  int max(int a, int b) {
      return a > b ? a : b;
  }
};

// 람다식 : 결국, 람다식은 객체다.
// 람다식의 참조변수 타입은..?
타입 obj = (a, b) -> a > b ? a : b

함수형 인터페이스

  • 단 하나의 추상 메서드만 선언된 인터페이스 (애너테이션 : @FunctionalInterface)

  • 함수형 인터페이스 타입의 참조 변수로 람다식을 참조할 수 있음.
    (단, 함수형 인터페이스의 메서드와, 람다식의 매개변수 개수와 반환타입이 일치해야 함.)

  • 람다식을 다루기 위해서 사용.

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;
                    }
				};
                
MyFunction f = (a, b) -> a > b ? a : b;  // new MyFunction() {...}과 동일

int value = f.max(3, 5);  // 실제로는 람다식 (익명 함수)이 호출됨


--------------------------------------------------------------------------------


@FunctionalInterface  // 함수형 인터페이스는 단 하나의 추상 메서드만 가져야 함.
interface MyFunction {
    void myMethod();  // 1. 람다식의 이름임. 람다식이 익명으로 구현한 함수.
}

// 함수형 인터페이스 타입의 매개변수
//				  ↓ 람다식을 매개변수로 받음
void aMethod(MyFunction f) {
	f.myMethod();  // 2. MyFunction에 정의된 메서드 호출 (람다식 호출)
}

MyFunction f = () -> System.out.println("myMethod()");  // 익명으로 myMethod()구현
aMethod(f);

aMethod(() -> System.out.println("myMethod()"));


// 함수형 인터페이스의 반환타입
// 이 메서드는 람다 식을 반환합니다.
MyFunction myMethod() {
	MyFunction f = () -> {};
    return f;
}

MyFunction myMethod() {
    return () -> {};
}

Predicate(함수형 인터페이스)의 결합

  • 인터페이스는 default 메서드, static 메서드, 추상 메서드를 가질 수 있다.

  • and(), or(), negate() { &&, ||, ! } 을 이용해서 두 Predicate를 하나로 결합 (default 메서드)

  • 등가 비교를 위한 Predicate 작성에는 isEqual()을 사용 (static 메서드)
    조건식임. 이미 구현이 돼있는 static 메서드임.
    Predicate.isEqual(str1) -> str1을 다른 것과 비교하기 위한 조건식이 만들어짐.
    Predicate.isEqual(str1).test(str2) -> str1과 str2를 비교

Predicate<Integer> p = i -> i < 100;
Predicate<Integer> q = i -> i < 200;
Predicate<Integer> r = i -> i % 2 == 0;

// 조건식들을 결합
Predicate<Integer> notP = p.negate();			// i >= 100; (!p)
Predicate<Integer> all = notP.and(q).or(r);		// 100 <= i && i < 200 || i % 2 == 0 
Predicate<Integer> all2 = notP.and(q.or(r));	// 100 <= i && (i < 200 || i % 2 == 0)

// Test
System.out.println(all.test(2));	// true
System.out.println(all2.test(2));	// false

// isEqual()
Predicate<String> p = Predicate.isEqual(str1);	// isEqual() : static 메서드 
boolean result = p.test(str2);	// str1과 str2가 같은지 비교한 결과 반환

// 간단히 쓰기 (str1 vs str2)
boolean result = Predicate.isEqual(str1).test(str2);

컬렉션 프레임웍 & 함수형 인터페이스

list.forEach(i -> System.out.print(i + ","));  // list의 모든 요소 출력
list.removeIf(x -> x % 2 == 0 || x % 3 == 0);  // 2 or 3의 배수 제거
list.replaceAll(i -> i * 10);  // 모든 요소에 10을 곱함

map.forEach((k, v) -> System.out.print("{" + k + ", " + v + "}, "));  // map의 모든 요소를 {k, v}의 형식으로 출력

메서드 참조

  • 하나의 메서드만 호출하는 람다식은 '메서드 참조'로 더 간단히 가능!

  • 클래스이름::메서드이름

  • static 메서드 참조
    (x) -> ClassName.method(x) => ClassName::metod

  • 인스턴스 메서드 참조
    (obj, x) -> obj.method(x) => ClassName::method

  • ex
    : Function<String, Integer> f = (String s) -> Integer.parseInt(s);
    => Function<String, Integer> f = Integer::parseInt;

  • 생성자의 메서드 참조
    Supplier<MyClass> s = () -> new MyClass(); => MyClass::new
    Function<Integer, MyClass> s = (i) -> new MyClass(i); => MyClass::new

  • 배열과 메서드 참조
    Function<Integer, int[]> f = x -> new int[x]; // 람다식
    Function<Integer, int[]> f2 = int[]::new; // 메서드 참조

profile
블로그 이동하였습니당! -> https://kimsy8979.tistory.com/

0개의 댓글