-
1. 람다식이란?
- 메서드를 하나의 식으로 표현
- 함수를 간략,명확한 식으로 표현 할 수 있게 해준다.
- 람다식으로 메서드를 표현하면, 이름과 반환값이 사라지므로, 익명함수라고도 한다.
int method() { return (int)(Math.random()*5) +1;} () -> {(int)(Math.random()*5)+1};
*모든 메서드는 기본적으로 클래스에 포함되어 있어야하므로, 필요한 메서드를 만드는 작업이 복잡하다.
*람다식은 이를 간결하게 처리할 수 있도록 해준다.
*람다식을 통해 매개변수를 전달하여 값을 return받을 수 도 있지만, 람다식 자체를 매개변수로 사용할 수 있다.
1.2 람다식 작성
- 메서드의 이름과 반환타입을 제거하고 선언부와 몸통부 사이에 ->를 추가한다.
- 반환값이 있는 메서드의 경우 return문을 식으로 대신할 수 있다. 식의 연산결과가 자동적으로 반환값이 된다.
(식으로 끝나면 ;를 사용하지 않는다. return을 사용하면 ;을 붙여야한다.)
- 매개변수 타입과 반환 타입은 대부분의 경우 생략가능(추론이 됨 -> 함수형 인터페이스 확인)
int max(int a, int b){ return a>b ? a: b;} (a,b) -> a>b? a:b void printVar(String name,int i){ System.out.println(//)} (String name, int i) -> System.out.println(//)
1.3 함수형 인터페이스
- 자바에서 모든 메서드는 클래스 내에 포함되어야한다.
- 람다식도 특정 클래스(익명 클래스)에 포함되어 있다.
- 람다식을 정의하고, 이를 담아서 사용할 인터페이스를 함수형 인터페이스라고 한다.
@FunctionalInterface interface MyFunc{ public int max (int a, int b); } //람다식을 사용할 기본 골격만 정의 //인터페이스를 구현하는 익명 클래스 구현 MyFunc f = new MyFunc(){ public int max (int a, int b) { return a>b ? a:b; } } //람다식을 통해 이를 간단하게 구현할 수 있다.(익명 객체를 람다식으로 대체) MyFunc f = (a,b) -> a>b?a:b //메서드를 구현해서 익명 클래스를 생성한 것일 뿐, 메서드를 사용하려면 아래와 같이 //매개변수를 넣고 함수를 호출해야하듯이 람다식도 동일하다. int maxnum = f.max(1,2);
* 위가 가능한 이유는 람다식도 익명 객체이고 MyFunc에서 구현한 익명 메서드와 람다식의 매개변수과 개수 반환값이 일치하기 때문이다.
*위의 구현은 자바의 규칙을 어기지 않고 자연스럽다. 위와같이 람다식을 다루기 위한 인터페이스를 함수형 인터페이스라고 한다.
** 함수형 인터페이스에는 하나의 추상메서드만 정의되어 있어야한다. (static, default메서드는 개수에 제약이 없다)
1.3.1 함수형 인터페이스 타입의 매개변수와 반환타입
- 특정 메서드의 매개변수가 MyFunc타입이면, 람다식을 그대로 넣어주거나, 람다식을 참조하는 참조변수를 넣어주면
된다.
void AMethod(MyFunc f) {f.max(1,2);} MyFunc f = (a,b) -> a>b ? a:b AMethod(f); // 반환 타입이 함수형 인터페이스일 수도 있다. Mtfunc myMethod() { //Myfunc f = () -> {}; //return f; return () -> {}}
1.3.2 람다식의 타입과 형변환
- 함수형 인터페이스로 람다식을 참조할 수 있는 것일 뿐, 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 아님
(람다식은 익명 객체 - 익명 객체는 타입이 없음)
- 정확히는 타입이 있지만, 컴파일러가 임의로 이름을 정한다.
MyFunc f = (MyFunc)(()->{}); //위와 같은 형변환이 필요하지만, 람다식이 함수형 인터페이스의 메서드를 정확하게 구현한다면 //형변환을 생략할 수 있다. //람다식은 함수형 인터페이스로만 형변환이 가능하다.
* 일반적인 익명 클래스는 외부클래스이름$번호와 같은 형식으로 타입이 결정
* 람다식 타입은 외부클래스이름$$Lambda$번호와 같은 형식으로 결정
1.3.3 외부 변수를 참조하는 람다식
- 람다식도 익명 객체, 익명 클래스의 인스턴스이므로 람다식 외부에서 선언된 변수에 접근하는 규칙은 익명 클래스와 같다.
- 람다식 내부에서 참조하는 외부지역변수는 final로 간주된다. 클래스의 인스턴스 변수는 상수로 간주하지 않는다.
- 외부 지역변수와 같은 람다식의 매개변수 허용하지 않는다.
class Outer { int val=10; class Inner{ int val =20; void method(int i){ // 람다식에서 i를 사용하면, i는 final int i취급 int val =30; // final int val i=10; //에러 상수의 값을 변경할 수 없음 Myfunc f = () -> { System.out.println(i); System.out.println(val); System.out.println(this.val); System.out.println(outer.this.val); } } } }
2. java.util.function패키지
- 대부분의 메서드의 매개변수와 반환타입은 비슷하다 (매개변수 1~2개, 반환값은 없거나 1개)
- 지네릭 메서드로 정의하면, 메서드의 매개변수나 반환타입이 달라도 상관없다.(그때그때 달라도됨)
- 따라서 function패키지에 자주쓰는것 정의해 둠
-크게 Supplier<t>, Consumer<t>, Function<T,R>, Predicate<T>, Operator<T>로 나눌 수 있다.
- 매개변수가 두개인 경우 위의 함수형 인터페이스에서 Bi를 붙인다.
- Runnable에 void run()도 함수형 인터페이스임 (매개변수없고 반환값 없음)
2.1 Supplier<T> 계열
- 매개변수는 없고, 반환값만 있다.
- 데이터를 공급해주는 공급자의 역할을 한다.(외부로 데이터를 리턴)
2.2 Consumer<T>
- 매개변수만 있고, 반환값이 없다.
- 파라미터를 입력받아 그것을 소비하는 interface이다.
- 함수내에서 매개변수를 사용하고, 반환값이 없음으로, 내부에서 매개변수를 소비한 것으로 의미를 부여한다.
2.3 Function<T,R>
- 일반적인 함수, 하나의 매개변수를 받아서 결과를 반환한다.
- Mapping 입력->출력을 연결하는 함수형 인터페이스이다.
- 입력타입과 출력타입은 다를 수도 같을 수도 있다. 주로 매개변수타입을 리턴타입으로 변환하는 역할을 한다.
2.4 Operator<T>
- 입력받은 타입과 동일한 타입을 출력하는 함수형 인터페이스이다.
- Funtion계열과 달리 입출력 타입이 다를 수 없다.
- 매개값을 이용하여 연산 후 동일한 타입으로 리턴값을 제공하는 역할을 한다.
2.5 Predicate<T> 계열
- 논리 판단을 해주는 함수형 인터페이스
- 입력을 받아서 boolean 타입 출력을 반환한다.
- 매개변수와 boolean 리턴값이있는 test() 메소드를 가지고 있다.
3. Function 합성 Predicate 결합
- 추상메서드 외에 default, static 메서드로 정의되어있는 메서드이다.
Function
default <V> Function<T,V> andThen (Function<? super R, ? extends V> after) f.andThen(g) : f를 먼저 적용하고, g를 적용한다. default <V> Function<V,R> compose(Function<? super V, ? extends T> before) f.compose(g) : g를 먼저 적용하고 f를 적용한다. static <T> Function<T,T> indentity() 이전과 이후가 동일한 항등함수가 필요할 때 사용한다. Function<String,Integer> f = (s) -> Integer.parseInt(s,16); Function<Integer,String> g = (i) -> Integer.toBinaryString(i); Function<String,String> h = f.andThen(g); Function<Integer,String> g = (i) -> Integer.toBinaryString(i); Function<String,Integer> f = (s) -> Integer.parseInt(s,16); Function<Integer,Integer> h = f.compose(g); Functoin<String,String> f = Function.identity(); // Function<String,String> f = x->x 와 같음
Predicate
default Predicate<T> and(Pridecate<? super T> other) default Predicate<T> or(Predicate<? super T> other) default Predicate<T> negate() static <T> Predicate<T> isEqual(Object targeRef) - Predicate의 논리 결합이다. 논리연산자로 &&,||,!사용하듯이 Predicate계열을 연결해서 사용한다.
4. 메서드참조
- 람다식이 하나의 메서드만 호출하는 경우 메서드 참조라는 방식을 사용할 수 있다.
Function<String,Integer> f = (s) -> Integer.parseInt(s) Function<String,Integer> f = Integer::parseInt;
- String을 Integer로 바꾸는 과정에서 매개변수가 Integer.parseInt를 통과하는 즉 람다식이 메서드 하나만 호출해서 사용하는 간단한 람다식이라면 메서드 참조를 통해 Integer::parseInt로 고칠 수 있다.
-컴파일러가 parseInt 메서드의 선언과 지네릭 타입을 통해 매개변수와 반환값을 추정해서 간결하게 표현할 수 있도록 한다.
BiFunction<String, String, Boolean> f = String::equals; //static 메서드 참조 (x) -> ClassName.method(x) ClassName::method //인스턴스 메서드 참조 (obj,xx) -> obj.method(x) ClassName::method //특정 객체 인스턴스 메서드 참조 (x) -> obj.method(x) obj::method
하나의 메서드만 호출하는 람다식은
클래스이름(or참조변수)::메서드명으로 바꿀 수 있다.
//생성자 메서드 참조 Supplier<MyClass> s = MyClass::new Function<Integer,MyClass> f =(i) -> new MyClass(i); Function<Integer,MyClass> f1 = MyClass::new; Function<Integer,int[]> f = int[]::new;
참고자료: 자바의 정석 -남궁성 저
https://hudi.blog/functional-interface-of-standard-api-java-8/
https://ttl-blog.tistory.com/207
'언어 > JAVA' 카테고리의 다른 글
참조타입,메모리,배열복사 (0) 2023.07.06 인터페이스 (0) 2023.07.06 객체지향 - 캡슐화 (0) 2023.06.12 자바 - 입출력(I/O) (1) 바이트기반 스트림/ 문자기반 스트림 (0) 2023.05.14 자바 - 열거형(enums) (0) 2023.03.26