람다식,함수형인터페이스,메서드참조
람다식을 말하기전에!
Java는 oop언어인데 jdk1.8부터 함수형언어를 지원함
대표적 함수형언어 : Haskell, Erlang, Scala
람다식이란?
* 함수(메서드)를 간단한 식(expression)으로 표현하는 방법
* 익명 함수(이름이 없는 함수, anonymous function) : 반환타입과 이름 제거
- 함수와 메서드의 차이 :
* 근본적으로 동일, 함수는 일반적 용어, 메서드는 객체지향개념 용어
* 함수는 클래스에 독립적, 메서드는 클래스에 종속적 (클래스에 종속적이다 클래스안에 들어가있어야한다 그래서 자바에서는 메서드라고함 ) 함수=메소드라고 생각하자
람다식 작성방법
1) 메서드의 이름, 반환타입 제거 '->'를 블록 앞에 추가, 익숙해지며 자연스럽게 사용할 수 있도록 숙달
2) 반환값 있는 경우, 식이나 값만 적고 return문 생략 가능
3) 매개변수와 타입이 추론가능 시 생략가능(대부분의 경우 생략가능)
int max(int a, int b){
return a > b ? a : b;
}
를 반환타입 int와 이름 max를 지우고 표현하는것
version 1
(int a , int b) -> {
return a > b ? a : b;
{
version 2
(a,b) -> a>b ? a : b
람다식 작성시 주의사항
1) 매개변수가 하나일 시 -> 괄호 생략가능(타입이 없을때만)
매개변수 타입이 없을때
(a) a로 생략가능
매개변수 타입이 있을때
(int a) O int a X ()생략불가능
2) 블록 안의 문장 하나뿐 -> 괄호 생략가능(끝에 ;를 안붙임)
단, 하나뿐인 문장이 return문이면 괄호{} 생략불가 < 신경 쓰지말자 거의다 return문 생략하기때문에 return이 없다면 {}생략 가능
(int i) -> {
sout;
}
위와같이 문장이 하나 뿐인 경우 {} 생략가능
(int i) -> sout << 이때 ; 를붙이지 않는다
람다식은 익명 함수? ㄴㄴ 익명 객체임
자바에서는 메소드만 따로 존재할 수 없으니 익명 함수가 아니라 익명 객체이다 익명 클래스의 객체인거임
(a,b) -> a > b ? a : b는 << 이것도 객체인것임 밑에가 변환되서 줄여진것뿐
new Object(){
int max(int a,int b) {
return a > b ? a : b;
}
}
와 같다. 위코드는 익명클래스의 익명객체인것이다.
객체의 선언과 생성을 동시에
람다식(익명 객체)을 다루기 위한 참조변수가 필요 참조변수의 타입은?
Object(타입) obj = new Object(){
int max(int a,int b) {
return a > b ? a : b;
}
}
obj가 참조변수
int value = obj.max(3,5); // 에러 Object 클래스에 max() 가 없음
Object라는 리모콘에는 max 버튼없음 그래서 object 리모콘으로 max 호출 불가
그래서 필요한게 함수형 인터페이스
함수형 인터페이스
단하나의 추상메서드만 선언된 인터페이스 (인터페이스에 모든 메소드는 public abstarct가 생략되어있음)
@FunctionalInterface < 이게붙여져있으면 함수형인터페이스가 올바르게 작성되있는지 확인해줌
안되있어도 인터페이스에 단하나의 추상메서드만선언되있어도 함수형인터페이스라고 부르기도함
interface MyFunction {
public abstract int max(int a, int b );
}
위 함수형 인터페이스를 구현 하면
MyFunction f = new MyFunction(){ << 익명클래스 (클래스선언 객체생성 동시에)
public int max(int a, int b){ << 여기에 public 왜붙여야하냐? 안붙이면 default interface는 기본 제어자가 public abstarct
return a > b ? a:b;
}
}
문법은
new 조상이름(클래스,인터페이스) { 멤버 }
마찬가지로 함수형인터페이싀 타입의 참조변수로 람다식을 참조할 수 있음
MyFuction f = (a,b) -> a>b ? a:b;
int value = f.max(3,5);
(a,b) -> a>b ? a:b; 가 구현체 interface의 구현체를 만들고
Myfunction이 참조하면 그것을 사용할 수 있게 되는것이다.
(a,b) -> a>b ? a:b;는 MyFuction의 추상메서드의 매개변수(개수,타입등)와 return 타입이 같아야한다.
그리고 (a,b) -> a>b ? a:b; 는 이름이 없음 이 객체에 함수를 쓰려면 이름을알아야하는데 그럼 쓸수없음 그
그래서 함수형인터페이스에 이름이 붙여져있는것이고 그것을 사용하는것
함수형인터페이스는 람다식을 다루기위해서 사용됨
람다식(익명객체)을 다루기 위한 참조 변수의 타입은 함수형 인터페이스로한다.
함수형 인터페이스 example
Collections.sort(list , new Comparaotr<>(){
public int compare(String s1,String s2){
return s2.compareTo(s1);
}
});
interface Comparator<T>{
int compare(T o1,T o2)
}
Collections.sort(list,(s1,s2) -> s2.comparetTo(s1));
함수형 인터페이스 타입의 매개변수,반환타입
함수형 인터페이스 타입의 매개변수
void aMethod(Myfunction f) {
f.myMethod(); // MyFunction에 정의된 메서드 호출
}
Myfunction f << 람다식(익명객체)을 받겠다
호출할때
Myfunction f = () - > sout;
aMethod(f);
또 줄이면
aMethod(() - > sout);
함수형인터페이스 타입의 반환타입
MyFunction myMethod(){
MyFunction f = () -> {};
return f;
}
위를 줄이면
MyFunction myMethod(){
return () -> {};
}
java.util.function 패키지
우리가 사용하는 함수형인터페이스가 몇개없음 그래서 그걸 미리 만들어서 놓은것
이렇게하면 다른사람이 이걸쓴걸 봤을때 이해하기 더욱 쉬워짐 < 표준화
- java.util.function 패키지 : 자주 사용하는 다양한 함수형 인터페이스 제공(표준화)
함수형인터페이스에 메서드가 선언되있고 메서드에 이름이있겠지만 어짜피 이름은 거의 생략되니 적지않음
* Runnable : 매개변수 x 반환값 x
* Supplier<T> : 매개변수 x 반환값 o (공급자라고생각 반환만함)
* Consumer<T> : Supplier와 반대로 매개변수 o 반환값 x (소비자 입력만함)
* Function<T, R> : 일반적 함수 하나의 매개변수 o 반환값 o (함수 입력도있고 출력도있고)
* Predicate<T> : 조건식을 표현하는데 사용, 매개변수 하나 반환 타입 boolean (조건식 입력값이 들어오면 bollean으로 반환)
매개변수 2개인 함수형 Bi가 두개를 뜻함
* BiConsumer<T,U> : 두 개의 매개변수 반환값 x
* BiPredicate<T,U> : 조건식을 표현하는데 사용 매개변수 둘, 반환값 boolean
* BiFunction<T,U,R> : 두 개의 매개변수를 받아서 하나의 결과 반환
매개변수가 3개인 함수형인터페이스는 없음 만약 필요하다면 직접 함수형 인터페이스를 선언해서 사용하면됨
@FunctionalInterface
interface TriFunction<T,U,V,R>{
R apply(T t,U u,V v);
}
매개변수 타입과 반환타입이 일치하는 함수형 인터페이스
* UnaryOperator<T> : Function의 자손, Function과 달리 매개변수의 결과의 타입이 같음 ,단항연산자
ex) int 형이들어온다면 int형이나감
* BinaryOperator<T> : BiFunction의 자손, BiFunction과 달리 매개변수와 결과의 타입이 같음 ,이항연산자
항등함수란? T를 넣으면 T가나오는걸 말함 수학에서 참고로만알아놓기
Predicate의 결합
1. and(), or(), negate() (< ! not을 의미) 로 두개 이상 Predicate를 하나로 결합( default 메소드임)
interface는 추상메소드(interface핵심)만 가졌지만 jdk1.8부터 static메소드와 default메소드가 추가됨
2. 등가비교를 위한 Predicate의 작성에는 isEqual()를 사용(static 메서드)
Fucntion 결합
- andThen() : 두 함수의 합성함수
- compose() : andThen()의 합성과는 반대로 합성
함수형 인터페이스를 사용하는 컬렉션 프레임웍의 메서드(와일드 카드 생략가능)
* Collection : removeIf(Predicate<E> filter) -> 조건에 맞는 요소 삭제
* List : replaceAll(UnaryOperator<E> operator) -> 모든 요소 변형 후 대체 ex) replaceAll(i->i*10) //모든요소에 10을곱함
* Iterable : forEach(Consumer<T> action) -> 모든요소에 action 수행
* Map : compute(K key, BiFunction<K,V,V> f) -> 지정된 키값에 f 수행
computeIfAbsent(K key, Function<K,V> f) -> 키가 없으면 작업 f 수행 후 추가
computeIfPresent(K key, BiFunction<K,V,V> f) -> 지정된 키가 없을 때 작업 f 수행
merge(K key, V value, BiFunction<K,V,V> f) -> 모든 요소에 병합작업 f 수행
forEach(BiConsumer<K,V> action) -> 모든 요소에 작업 action 수행
replaceAll(BiFunction<K,V,V> f) -> 모든 요소에 치환작업 f 수행
메서드 참조 (Method reference)
하나의 메서드만 호출하는 람다식은 메서드 참조로 간단히 할 수 있다. 클래스이름::메서드이름 으로 쓰임
* static메서드 참조 : (x) -> ClassName.method(x) (람다), ClassName::method (메서드참조)
* instance method참조 : (obj, x) -> obj.method(x), ClassName::method
* 특정 객체 인스턴스 메서드 참조 (x) -> obj.method(x) , obj::method <<이건거의 안씀 신경쓰지마셈
ex
Function<String,Integer> f = (String s) -> Integer.parseInt(s)
String 입력값, Integer 출력값을암 String s 지울수있음 뒤에 s 도지움
그리고 그것을 간단하게 표현할때 Integer::parseInt로 씀
Function<String,Integer> f = Integer::parseInt;
* 메서드 참조가 이해가 안간다면 ?
다시 람다식으로 돌려보자
메서드참조 클래스이름::메서드이름 > 이것을 다시 람다식으로 바꿔보면 메서드참조가 더 이해 잘될거다.
바꿀때 어떤정보를 이용? 함수형인터페이스가 어떤게 쓰인건지 보고 이용해봐라 , 쓰인 메서드에 매개변수가 몇개인지 확인
- 생성자의 메서드 참조
Supplier<MyClass> s () -> new MyClass(); //Suppelier 공급자 MyClass가 return타입임 그니까 아래와같이 간단하게 바꿀수있음
Supplier<MyClass> s = MyClass::new;
Supplier말고도 Fucntion 등을 이용해서도 생성자의 메서드 참조가능
-배열의 메서드 참조
Function<Integer,int[]> f = x-> new int[x];
Function<Integer,int[]> f = int::new;