자바는 "객체 지향 언어(OOP)" 이지만, 함수형 언어(FP)를 추가하였음.
FP 대표 : Haskell, Erlang, Scala
Python, js : OOP + FP
함수(메서드)를 간단한 "식"으로 표현하는 방법
익명 함수, 이름이 없는 함수 라고도 한다.
함수 vs 메서드
작성법
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
(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 () -> {};
}
인터페이스는 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; // 메서드 참조