Back-End/[S1]JAVA

람다식,함수형인터페이스,메서드참조

아메바개발 2022. 10. 29. 19:11

람다식을 말하기전에!

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;