Java : OOP 언어
JDK1.8부터 함수형 언어의 기능을 추가하였다. 이로 인해 함수형 언어의 장점을 자바에서도 누릴 수 있게 되었다.
람다식(Lambda Expression)
함수(메서드)를 간단한 '식(Expression)'으로 표현한 것이다.
람다식을 익명 함수라고도 한다.
함수와 메서드의 차이
근본적으로는 동일하다. 함수는 일반적인 용어이며, 메서드는 객체지향개념의 용어이다.
함수는 클래스에 독립적이고, 메서드는 클래스에 종속적이다.
람다식 작성하기
메서드의 이름과 반환타입을 제거하고 매개변수 선언부와 몸통{ } 사이에 '->'를 추가한다.
반환값이 있는 경우, return문 대신 식이나 값으로 대신할 수 있다. '문장'이 아닌 '식'이므로 끝에 ';'를 붙이지 않는다.
람다식에 선언된 매개변수의 타입은 추론이 가능하다면 생략할 수 있다. 대부분의 경우 생략이 가능하다.
주의사항
매개변수가 하나인 경우, 괄호를 생략할 수 있다. 단, 매개변수의 타입이 없을 때만 가능하다.
블록 안의 문장이 하나일 때는 { }를 생략할 수 있다. 끝에 ';'를 붙이지 않는다.
함수형 인터페이스
람다식은 익명 함수가 아니라 익명 객체이다.
오른쪽의 익명 객체를 람다식으로 간단히 할 수 있는 것이다.
람다식을 다루기 위해서는 참조변수가 필요하다.
타입을 Object로 한다면
int value = obj.max(3,5);
위 코드는 Object클래스에 max()가 없기 때문에 obj에 max()가 있어도 호출할 수 없어서 에러가 발생한다. 그래서 람다식은 인터페이스를 통해 다룬다.
함수형 인터페이스
단 하나의 추상 메서드만 선언된 인터페이스이다.
이제
int value = f.max(3,5);
이 코드는 에러가 발생하지 않는다.
함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있다.
MyFunction f = (a, b) -> a > b ? a : b;
int value = f.max(3,5); // 실제로는 람다식이 호출된다.
단, 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야 한다. 그래야 람다식과 인터페이스의 메서드가 1:1로 연결될 수 있기 때문이다.
@FunctionallInterface를 붙이면 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해 주므로, 꼭 붙이자
함수형 인터페이스 타입의 매개변수와 반환타입
@FunctionalInterface
interface MyFunction {
void myMethod();
}
void aMethod(MyFunction f) {
f.myMethod(); // 람다식 호출
}
메서드의 매개변수가 MyFunction타입이면, 이 메서드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정해야 한다는 뜻이다.
MyFunction f = () -> System.out.println("myMethod()");
aMethod(f);
"myMethod()"라는 문자열을 출력하는 람다식(f)을 aMethod()에 넣어주는 것이다. 그러면 aMethod(f)는 f.myMethod()를 호출한다.
위 두 코드를 하나로 합치면 아래와 같다.
aMethod(() -> System.out.println("myMethod()"));
메서드의 반환타입이 함수형 인터페이스타입이라면,
MyFunction myMethod() {
MyFunctionn f = () -> {};
return f;
// 위 두줄을 하나로 합치면
// return () -> {};
}
이 함수형 인터페이스의 추상메서드와 동등한 람다식을 가리키는 참조변수를 반환하거나 람다식을 직접 반환할 수 있다.
람다식의 타입과 형변환
- 함수형 인터페이스로 람다식을 참조할 수 있지만, 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 아님
- 람다식은 익명 객체이기 때문에 타입이 없다. (타입은 있지만 컴파일러가 임의로 이름을 정함)
- 대입 연산자의 양변의 타입을 일치시키기 위해 형변환이 필요
MyFunction f = (MyFunction) ( () -> {} );
람다식은 Object타입으로 형변환 할 수 없다. 굳이 한다면 먼저 함수형 인터페이스로 변환해야 한다.
Object obj = (Object)(MyFunction) ( () -> {} );
java.util.function 패키지
자주 사용되는 다양한 함수형 인터페이스를 제공한다.
Predicate<String> isEmptyStr = s -> s.length()==0;
String = "";
if(isEmptyStr.test(s)) // if(s.length()==0
System.out.println("This is an empty String.");
매개변수가 두 개인 함수형 인터페이스
두 개 이상의 매개변수를 갖는 함수형 인터페이스가 필요하다면 직접 만들어서 써야 한다.
매개변수의 타입과 반환타입이 일치하는 함수형 인터페이스
UnaryOperator : 단항 연산자
BinaryOperator : 이항 연산자
컬렉션 프레임웍과 함수형 인터페이스
기본형을 사용하는 함수형 인터페이스
Function의 합성과 Predicate의 결합
java.util.function패키지의 함수형 인터페이스에는 추상메서드 외에 디폴트 메서드와 static메서드도 있다.
Function
default <V> Function<T,V> andThen(Function<? super R, ? extends V> after)
default <V> Function<V,R> compose(Function<? super V, ? extends T> before)
static <T> Function<T,T> identity()
Predicate
default Predicate<T> and(Predicate<? super T> other)
default Predicate<T> or(Predicate<? super T> other)
default Predicate<T> negate()
static <T> Predicate<T> isEqual(Object targetRef)
Function의 합성
Function타입의 두 람다식을 하나로 합성해서 새로운 람다식을 만들 수 있다.
andThen()
f.andThen(g)
compose()
f.compose(g)
Predicate의 결합
여러 Predicate를 and(), or(), negate()로 연결해서 하나의 새로운 Predicate로 결합할 수 있다.
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
// 100 <= i && (i < 200 || i%2 == 0)
Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150)); // true
두 대상을 비교하는 Predicate의 작성에는 isEqual()를 사용한다.(static메서드)
Predicate<String> p = Predicate.isEqual(str1);
boolean result = p.test(str2); // str1과 str2가 같은지 비교하여 결과를 반환
// 위 두 코드를 하나로
boolean result = Predicate.isEqual(str1).test(str2);
메서드 참조
람다식이 하나의 메서드만 호출하는 경우에는 '메서드 참조'로 람다식을 간단히 할 수 있다.
하나의 메서드만 호출하는 람다식은
'클래스이름::메서드이름' 또는 '참조변수::메서드이름'으로 바꿀 수 있다.
생성자의 메서드 참조
'JAVA' 카테고리의 다른 글
[지네릭스] 지네릭스 (0) | 2023.03.15 |
---|---|
스트림(Stream) (0) | 2023.03.14 |
[java.math] BigInteger, BigDecimal (0) | 2023.03.09 |
[java.util] Objects, Random, 정규식 (0) | 2023.03.09 |
[java.lang] Wrapper 클래스, Number클래스 (0) | 2023.03.09 |