chapter 14
람다식
람다식이란?
Runnable runnable = () -> {...}; // 람다식
- 위 코드는 Runnable 변수에 대입되므로 람다식은 Runnable의 익명 구현 객체를 생성하게 된다.
=>람다식은 함수적 프로그래밍 언어로 “(매개변수)->{실행코드}” 형태로 작성된다. 병렬 처리와 이벤트 지향 프로그래밍에 적합한 언어로 자바에서는 한때는 주목받지 못하다가 요즘들어 다시 각광받고 있다. 각광받는 이유는 람다식을 사용하면 자바 코드가 간결해지고, 컬렉션의 요소를 필터링하거나 매핑해서 원하는 결과를 쉽게 집계할 수 있기 때문이다.
- 람다식은 마치 함수 정의 형태를 띠고 있지만, 런타임 시에 인터페이스의 익명 구현 객체로 생성됨을 명심하자. 어떤 인터페이스가 구현될 것인가는 대입되는 인터페이스가 무엇이냐에 달려있다.
타겟 타입과 함수적 인터페이스
=> 람다식의 형태가 매개변수가 있는 블록이기 때문에 마치 자바에서 직접 메소드를 선언한 것처럼 보이지만 자바는 메소드를 단독으로 선언 할 수 없기 때문에람다식은 단순히 메소드를 선언하는 것이 아니라 이 메소드를 가지고 있는 객체를 생성해낸다.(명심!)
인터페이스 변수 = 람다식;
- 람다식은 인터페이스의 종류에 따라 작성 방법이 달라지기 때문에 람다식이 대입될 인터페이스를 람다식의 타겟 타입(target type)이라고 한다.
함수적 인터페이스(@FunctionalInterface)
=> 모든 인터페이스는 람다식의 타켓 타입으로 사용할 수 없다. 람다식이 하나의 메소드를 정의하기 때문에 두 개 이상의 추상메소드가 선언된 인터페이스는 람다식을 이용해서 구현 객체를 생성할 수 없다. 하나의 추상 메소드가 선언된 인터페이스만이 람다식의 타겟 타입이 될 수 있는데 , 이러한 인터페이스를 함수적 인터페이스(functional interface)라고 한다.
-
하나의 추상메소드가 선언된 인터페이스만이 람다식의 타겟 타입이 될 수 있고 이러한 인터페이스를 함수적 인터페이스(functional interface)라고 한다.
-
두 개 이상의 추상 메소드가 선언되지 않도록 컴파일러가 체킹해주는 기능이 있는데, 인터페이스 선언 시 @FunctionalInterface 어노테이션을 붙이면 된다. 이 어노테이션은 두 개 이상의 추상 메소드가 선언되면 컴파일 오류가 발생시킨다.
=> @FunctionInterface 어노테이션은 선택사항이다. 단, 실수로 두 개이상의 추상 메소드를 선언하는 것을 방지하고 싶다면 붙여주는 것이 좋다.
리턴값과 매개변수가 없는 람다식
``java
public interface MyFunctionalInterface {
void method(); // 추상 메소드
public static void main(String[] args) {
//매개변수와 리턴 값이 없는 메소드
MyFunctionalInterface fi = ()->{
System.out.println("hello");
};
fi.method();
} } ```
매개변수가 있는 람다식
public interface ParameterInterface {
//매개변수가 있는 추상 메소드
void method(int x);
public static void main(String[] args) {
//매개변수가 하나인 경우에는 괄호를 생략할 수 있다.
ParameterInterface fi = x-> System.out.println("hello "+x);
fi.method(5);
}
}
리턴값이 있는 람다식
public interface ReturnInterface {
//리턴 값이 있는 추상 메소드
int method();
public static void main(String[] args) {
//리턴값이 있는 람다식
ReturnInterface fi = ()->{
return 5;
};
int x = fi.method();
System.out.println("hello "+ x);
}
}
매개변수와 리턴값이 있는 람다식
public interface ParameterReturnInterface {
//리턴값과 매개변수가 둘다 있는 추상 메소드
int add(int x, int y);
public static void main(String[] args) {
// 리턴값과 매개변수가 있는 람다식
ParameterReturnInterface fi = (x, y) ->{
return x+y;
};
int result = fi.add(3,5);
System.out.println("3 과 5를 더하면 " + result+ "입니다.");
}
}
클래스 멤버와 로컬 변수 사용
클래스의 멤버 사용
=> 람다식 실행 블록에는 클래스의 멤버인 필드와 메소드를 제약 없이 사용할 수 있는데, this의 사용에는 주의를 기울이자. 일반적으로 익명 객체 내부에서 this는 익명 객체의 참조이지만, 람다식에서 this는 내부적으로 생성되는 익명 객체의 참조가 아니라 람다식을 실행한 객체의 참조이다.
public class UsingThis{
public int outterfield = 10;
class Inner{
int innerField = 20;
void method(){
//Ramda
MyFunctionInterface fi = () -> {
/* 람다식에서의 this는 내부적으로 생성되는 익명 객체의 참조가 아니라 람다식을 실행한 객체의 참조이다. 즉 Inner 클래스 인스턴스의 참조이다 .*/
//바깥 객체의 참조를 얻기 위해서는 클래스명 this를 사용
System.out.println("outterField: " + UsingThis.this.outterField + "\n");
//람다식 내부에서 this는 Inner 객체를 참조
System.out.println("innnerField: " + this.innerField + "\n");
};
}
}
}
로컬 변수 사용
- 람다식에서 바깥 클래스의 필드나 메소드는 제한 없이 사용할 수 있으나, 메소드의 매개 변수 또는 로컬 변수를 사용하면 이 두 변수는 final 특성을 가져야 한다. (매개변수나 로컬 변수는 함수가 종료되면 소멸되고 도중에 값이 바뀌면 안되므로 final 적인 특성을 가져야 한다.)
public class UsingLocalVariable {
void method(int arg){ //arg는 매개변수이기 때문에 final 한 특성을 가짐.
int localVar = 40; // localVar은 로컬 변수이기 때문에 final한 특성을 가짐
//arg = 5;(x) : arg는 람다식에서 사용하므로 매개변수로서 final한 특성을 가져야 하므로 값이 변경될 수 없다.
//localVar = 20;(x) : localVar 은 람다식에서 사용하므로 로컬 변수로서 final한 특성을 가져야 하기 때문에 값이 변경 될 수 없다.
MyFunctionalInterface fi = () ->{
//로컬 변수 읽기
System.out.println("arg: " + arg);
System.out.println("localVar: " + localVar);
};
fi.method();
}
표준 API의 함수적 인터페이스
- 자바에서 제공되는 표준 API에서 한개의 추상 메소드를 가지는 인터페이스는 모두 람다식이 적용 가능하다. 예를 들어 스레드의 작업을 정의하는 Runnable 인터페이스는 매개변수와 리턴값이 없는 run()메소드만 존재하기 때문에 다음과 같이 람다식을 이용해서 Runnable 인스턴스를 생성시킬 수 있다.
public class RunnableExample {
public static void main(String args[]){
// 이 람다식은 Runnable 인터페이스의 run() 메소드를 나타낸다.
// 즉, 스레드가 실행하는 코드이다.
Runnable runnable = () ->{
for(int i=0; i<10; i++){
System.out.println(i);
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
* **Thread 생성자를 호출할 때 다음과 같이 람다식을 매개값으로 대입해도 된다.(대입하자)**
```java
Thread thread = new Thread(()->{
for(int i=0; i<10; i++){
System.out.println(i);
}
});
- 자바 8부터는 빈번하게 사용되는 함수적 인터페이스(functional interface)는 java.util.function 표준 API 패키지로 제공된다. java.util.function 패키지의 함수적 인터페이스는 크게 Consumer, Supplier, Function, Operator, Predicate 로 구분된다. 구분 기준은 인터페이스에 선언된 추상 메소드의 매개값과 리턴값의 유무이다.
종류 | 추상 메소드 특징 |
---|---|
Consumer | -매개값은 있고, 리턴값은 없음 |
Supplier | -매개값은 없고, 리턴값은 있음 |
Function | -매개값도 있고, 리턴값도 있음 |
-주로 매개값을 리턴값으로 매핑(타입 변환) | |
Operator | -주로 매개값도 있고, 리턴값도 있음 |
-주로 매개값을 연산하고 결과를 리턴 | |
Predicate | -매개값은 있고, 리턴 타입은 boolean |
-매개값을 조사해서 true/false를 리턴 |
Consumer 함수적 인터페이스
=> Consumer 함수적 인터페이스의 특징은 리턴값이 없는 accept() 메소드를 가지고 있다. accept() 메소드는 단지 매개값을 소비하는 역할만 한다. 여기서 소비한다는 말은 사용만 할 뿐 리턴값이 없다는 뜻이다.
- 매개 변수의 타입과 수에 따라서 아래와 같은 Consumer들이 있다.
인터페이스명 | 추상 메소드 | 설명 |
---|---|---|
Consumer<T> | void accept(T t) | 객체 T를 받아 소비 |
BiConsumer<T,U> | void accept(T t, U u) | 객체 T 와 U 를 받아 소비 |
DoubleConsumer | void accept(double value) | double 값을 받아 소비 |
IntConsumer | void accept(int value) | int 값을 받아 소비 |
LongConsumer | void accept(long value) | long 값을 받아 소비 |
ObjDoubleConsumer<T> | void accept(T t, double value) | 객체 T 와 double 값을 받아 소비 |
ObjIntConsumer<T> | void accept(T t, int value) | 객체 T 와 int 값을 받아 소비 |
ObjLongConsumer<T> | void accept(T t, Long value) | 객체 T 와 long 값을 받아 소비 |
예제
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.ObjIntConsumer;
public class ConsumerExample {
public static void main(String[] args) {
Consumer<String> consumer = t -> System.out.println(t + "8");
consumer.accept("java");
BiConsumer<String, String> biConsumer = (t,u) -> System.out.println(t+u);
biConsumer.accept("java","8");
DoubleConsumer doubleConsumer = (t) -> System.out.println("java"+t);
doubleConsumer.accept(8.0);
ObjIntConsumer<String> objIntConsumer = (t,i) -> System.out.println(t+i);
objIntConsumer.accept("java",8);
}
}
Supplier 함수적 인터페이스
=> Supplier 함수적 인터페이스의 특징은 매개 변수가 없고 리턴값이 있는 getXXX() 메소드를 가지고 있다. 이 메소드들은 실행 후 호출한 곳으로 데이터를 리턴(공급)하는 역할을 한다.
인터페이스 형 | 추상 메소드 | 설명 | |
---|---|---|---|
Supplier<T> | T get() | T 객체를 리턴 | T 객체를 리턴 |
BooleanSupplier | boolean getAsBoolean() | boolean 값을 리턴 | |
DoubleSupplier | double getAsDouble() | double 값을 리턴 | |
IntSupplier | int getAsInt() | int값을 리턴 | |
LongSupplier | long getAsLong() | long값을 리턴 |
import java.util.function.IntSupplier;
public class SupplierExample {
public static void main(String[] args) {
IntSupplier intSupplier = ()->{
//1~6에서 random한 숫자가 나올 것.
return (int)(Math.random()*6) + 1;
};
int num = intSupplier.getAsInt();
System.out.println(num);
}
Functuon 함수적 인터페이스
=> Function 함수적 인터페이스의 특징은 매개값과 리턴값이 있는 applyXXX() 메소드를 가지고 있다. 이 메소드들은 매개값을 리턴값으로 매핑(타입 변환)하는 역할을 한다.
인터페이스명 | 추상 메소드 | 설명 |
---|---|---|
Function<T,R> | R apply(T t) | 객체 T를 객체 R로 매핑 |
BiFunction<T,U,R> | R apply(T t,U u) | 객체 T와 U를 객체 R로 매핑 |
DoubleFunction<R> | R apply(double value ) | double을 객체 R 로 매핑 |
IntFunction<R> | R apply(int value) | int를 객체 R로 매핑 |
IntToDoubleFunction | double applyAsDouble(int value) | int를 double 로 매핑 |
IntToLongFunction | long applyAsLong(int value) | int를 long으로 매핑 |
LongToDoubleFunction | double applyAsDouble(long value) | long을 double로 매핑 |
LongToIntFunction | double applyAsDouble(long value) | long을 double로 매핑 |
ToDoubleBiFunction<T,U> | double applyAsDouble(T t, U t) | 객체 T와 U를 double로 매핑 |
ToDoubleFunction<T> | double applyAsDouble(T t) | 객체 T를 double로 매핑 |
ToIntBiFunction<T,U> | int applyAsInt(T t, U t) | 객체 T와 U를 int로 매핑 |
ToIntFunction<T> | int applyAsInt(T t, U t) | 객체 T를 int로 매핑 |
ToLongBiFunction<T,U> | long applyAsLong(T t, U t) | 객체 T와 U를 long으로 매핑 |
ToLongFunction<T,U> | long applyAsLong(T t) | 객체 T를 long으로 매핑 |
Function<Student, String > function = t -> {return t.getName();}
또는
Function<Student, String > function = t -> t.getName();
=> 리턴문만 있을 경우 중괄호 {} 와 return문은 생략할 수 있다.
ToIntFunction<Student> function = t-> { return t.getScore();}
또는
ToIntFunction<Student> functoin = t -> t.getScore();
=> 이 경우도 역시 리턴문만 있을 경우 중괄호 {}와 return문은 생략할 수 있다.
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;
public class FunctionExample1 {
private static List<Student> list = Arrays.asList(
new Student("홍길동", 90, 96),
new Student("김도형", 100, 100)
);
//function에 인자가 t-> t.getName();으로 전달되므로 Student의 getName()이 리턴된다.
private static void printString(Function<Student, String> function) {
for (Student student : list) {
System.out.println(function.apply(student) + " ");
}
System.out.println();
}
//function에 인자가 t-> t.getEnglishScore();으로 전달되므로 Student의 getEnglishScore()이 리턴된다.
private static void printInt(ToIntFunction<Student> function) {
for(Student student: list) {
System.out.println(function.applyAsInt(student));
}
System.out.println();
}
//function에 인자가 t-> t.getMathScore();으로 전달되므로 Student의 getMathScore()이 리턴된다.
public static void main(String[] args) {
System.out.println("[학생 이름]");
FunctionExample1.printString(t->t.getName());
System.out.println("[영어 성적]");
FunctionExample1.printInt(t->t.getEnglishScore());
System.out.println("[수학 성적]");
FunctionExample1.printInt(t->t.getMathScore());
}
}
import java.util.Arrays;
import java.util.List;
import java.util.function.ToIntFunction;
public class FunctionExample2 {
private static List<Student> list = Arrays.asList(
new Student("홍길동", 90,90),
new Student("김도형", 100, 100)
);
private static double avg(ToIntFunction<Student> function){
int sum =0;
for(Student student: list){
sum+= function.applyAsInt(student);
}
return (double) sum/ list.size();
}
public static void main(String[] args){
System.out.println("[영어 평균]");
System.out.println(avg(t->t.getEnglishScore()));
System.out.println("[수학 평균]");
System.out.println(avg(t->t.getMathScore()));
}
}
Operator 함수적 인터페이스
=> Operator 함수적 인터페이스는 Function 와 동일하게 매개 변수와 리턴 값이 있는 applyXXX() 메소드를 가지고 있다. 하지만 이 메소드들은 매개값을 리턴값으로 매핑(타입 변환)하는 것보다 매개값을 이용해서 연산을 수행한 후 동일한 타입으로 리턴값을 제공하는 역할을 한다.
인터페이스명 | 추상 메소드 | 설명 |
---|---|---|
BinaryOperator<T> | T apply(T t, T t) | T와 T를 연산한 후 T 리턴 |
UnaryOperator<T> | T apply(T t) | T를 연산한 후 T 리턴 |
DoubleBinaryOperator | double applyAsDouble(double ,double) | 두 개의 double 연산 |
DoubleUnaryOperator | double applyAsDouble(double) | 한 개의 double 연산 |
IntBinaryOperator | int applyAsInt(int , int) | 두 개의 int 연산 |
IntUnaryOperator | int applyAsInt(int) | 한 개의 int 연산 |
LongBinaryOperator | long applyAsLong(long, long) | 두 개의 long 연산 |
LongUnaryOperator | long applyAsLong(long) | 한 개의 long 연산 |
import java.util.function.IntBinaryOperator;
public class OperatorExample {
private static int[] scores = {92,95,87};
//IntBinaryOperator : 두 개의 int 연산.
private static int maxOrMin(IntBinaryOperator operator){
int result = scores[0];
for(int score: scores){
result = operator.applyAsInt(result,score);
}
return result;
}
public static void main(String[] args) {
// 최댓값 구하기
int Max = maxOrMin((a,b)->{
if(a>=b) return a;
else return b; }
);
//최소값 구하기
int Min = maxOrMin((a,b)->{
if(a>=b) return b;
else return a;
});
System.out.println("최댓값: " + Max);
System.out.println("최솟값: " + Min);
}
}
Predicate 함수적 인터페이스 => Predicate 함수적 인터페이스는 매개 변수와 boolean 리턴값이 있는 testXXX() 메소드를 가지고 있다. 이 메소드들은 매개값을 조사해서 true 또는 false를 리턴하는 역할을 한다.
인터페이스명 | 추상 메소드 | 설명 |
---|---|---|
Predicate<T> | boolean test(T t) | 객체 T를 조사 |
BiPredicate<T,U> | boolean test(T t, U u) | 객체 T 와 U를 조사 |
DoublePredicate | boolean test(double value) | double 값을 조사 |
IntPredicate | boolean test(int value) | int 값을 조사 |
LongPredicate | boolean test(long value) | long 값을 조사 |
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateExample {
private static List<Student2> list = Arrays.asList(
new Student2("Kim", "man", 90),
new Student2("Jang", "woman", 95),
new Student2("Ham", "man",84),
new Student2("Park", "woman", 88)
);
private static double arg(Predicate<Student2> predicate){
int count =0, sum =0;
for(Student2 student: list) {
if (predicate.test(student)) {
count++;
sum += student.getScore();
}
}
return (double)sum/count;
}
public static void main(String[] args) {
double arg_Man= arg(t->t.getSex().equals("man"));
double arg_Woman = arg(t-> t.getSex().equals("woman"));
System.out.println(arg_Man);
System.out.println(arg_Woman);
}
}
andThen() 과 compose() 디폴트(default) 메소드
-
Consumer, Function, Operator 종류의 함수적 인터페이스는 andThen()과 compose() 디폴트 메소드를 가지고 있다. andThen()과 compose() 디폴트 메소드는 두 개의 함수적 인터페이스를 순차적으로 연결하고, 첫 번째 처리 결과를 두번째 매개값으로 제공해서 최종 결과값을 얻을 때 사용한다.
-
andThen()과 compose()의 차이점은 어떤 함수적 인터페이스부터 먼저 처리하느냐이다.
andThen() 코드
인터페이스AB = 인터페이스A.andThen(인터페이스B);
최종결과 = 인터페이스AB.method();
=> 인터페이스AB의 method()를 호출하면 우선 인터페이스A부터 처리하고 결과를 인터페이스 B의 매개값으로 제공한다. 인터페이스 B는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴한다.
compose() 코드
인터페이스 AB = 인터페이스A.compose(인터페이스 B);
최종 결과 = 인터페이스AB.method();
=> compose()는 인터페이스 AB의 method()를 호출하면 우선 인터페이스 B부터 처리하고 결과를 인터페이스A의 매개값으로 제공한다. 인터페이스 A는 제공받은 매개값을 가지고 처리한 후 최종 결과를 리턴한다.
-
Consumer의 순차적 연결 => Consumer 종류의 함수적 인터페이스는 처리 결과를 리턴하지 않기 때문에 andThen() 디폴트 메소드는 함수적 인터페이스의 호출순서만 정한다. 다음 예제는 Consumer<Member> 함수적 인터페이스 두개를 순차적으로 연결해서 실행한다.
-
Function의 순차적 연결
=> Function과 Operator 종류의 함수적 인터페이스는 먼저 실행한 함수적 프로그래밍의 결과를 다음 함수적 인터페이스의 매개값으로 넘겨주고, 처리 결과를 리턴한다. 예를 들어, Function<Member, Address>와 Function<Address, String> 을 순차적으로 연결해서 Function<Member,String>을 생성할 수 있다.
=> Function<Member,Address> + Function<Address, String> = Function<Member,String>이 되는데 ,즉 Address는 두 함수적 인터페이스 간의 전달 데이터이고, 최종 함수적 인터페이스의 형태는 입력 데이터가 Member, 출력 데이터가 String이 되는 Function<Member,String> 이 된다.
import java.util.function.Function;
public class FunctionAndThenComposeExample {
public static void main(String[] args) {
Function<Member,Address> functionA = m ->m.getAddress();
Function<Address,String> functionB = a -> a.getCity() ;
String city;
Function<Member,String> functionAB = functionA.andThen(functionB);
city=functionAB.apply(new Member("홍길동", "hong", new Address("korea", "seoul")));
System.out.println(city);
functionAB = functionB.compose(functionA);
city = functionAB.apply(new Member("김도형","kimdo", new Address("korea","suwon")));
System.out.println(city);
}
}
and(),or(), negate() 디폴트 메소드
Predicate 종류의 함수적 인터페이스는 and(), or(), negate() 디폴트 메소드를 가지고 있다. 이 메소드들은 각각 논리 연산자인 **&&, , !**과 대응된다고 볼 수 있다.
-
and()메소드는 두 Predicate 모두 true를 리턴하면 최종적으로 true를 리턴하는 Predicate를 생성한다.
-
or()메소드는 두 Predicate 중 하나가 true를 리턴하면 최종적으로 true를 리턴하는 Predicate를 생성한다.
-
negate()메소드는 하나의 Predicate가 true를 리턴하면 최종적으로 false 를 리턴하고 false를 리턴하면 true를 리턴하는 Predicate 를 생성한다.
import java.util.function.IntPredicate;
public class PredicateAndOrNagateExample {
public static void main(String[] args) {
//2의 배수 검사
IntPredicate predicateA = a -> a%2 == 0;
//3의 배수 검사
IntPredicate predicateB = a -> a%3 == 0;
IntPredicate predicateAB;
boolean result;
predicateAB = predicateA.and(predicateB);
result = predicateAB.test(9);
System.out.println("9는 2와 3의 배수입니까? " +result);
predicateAB = predicateA.or(predicateB);
result = predicateAB.test(9);
System.out.println("9는 2나 3의 배수입니까? " +result);
predicateAB = predicateA.negate();
result = predicateAB.test(9);
System.out.println("9는 2의 배수입니까? " +!result);
predicateAB = predicateB.negate();
result = predicateAB.test(9);
System.out.println("9는 3의 배수입니까? "+ !result);
}
}
isEqual() 정적 메소드
- Predicate<T> 함수적 인터페이스는 이외에 isEqual() 메소드를 제공한다. isEqual() 메소드는 test() 매개값인 sourceObject 와 isEqual()의 매개값인 targetObject를 java.util.Objects 클래스의 equals()의 매개값으로 제공하고, Objects.equals(sourceObject, targetObject)의 리턴값을 얻어 새로운 Predicate<T>를 생성한다.
Predicate<Object> predicate = Predicate.isEqual(targetObject);
boolean result = predicate.test(sourceObject);
//Objects.equals(sourceObject, targetSource) 로 값 리턴!
- Objects.equals(sourceObject, targetObject)는 sourceObject, targetObject 둘 다 null 일때 true, 둘 중 하나만 null이면 false, 둘 다 null이 아니면 sourceObject.isEquals(targetObject)의 리턴값을 리턴한다.
import java.util.function.Predicate;
public class PredicateIsEqualExample {
public static void main(String[] args) {
Predicate<String> predicate;
predicate = Predicate.isEqual(null);
System.out.println("null, null: " + predicate.test(null));
predicate = Predicate.isEqual(null);
System.out.println("null, not null: "+ predicate.test("java8"));
predicate = Predicate.isEqual("java8");
System.out.println("not null, null: " + predicate.test(null));
predicate = Predicate.isEqual("java8");
System.out.println("not null, not null: " + predicate.test("java8"));
}
}
minBy(), maxBy() 정적 메소드
- BinaryOperator<T> 함수적 인터페이스는 minBy()와 maxBy() 정적 메소드를 제공한다. 이 두 메소드는 매개값으로 제공되는 Comparator를 이용해서 최대T와 최소T를 얻는 BinaryOperator<T> 를 리턴한다.
리턴 타입 | 정적 메소드 |
---|---|
BinaryOperator<T> | minBy(Comparator<? super T> comparator) |
BinaryOperator<T> | maxBy(Comparator<? super T> comparator) |
- Comparator<T>는 다음과 같이 선언된 함수적 인터페이스이다. o1과 o2를 비교해서 o1이 작으면 음수를 , 둘이 같으면 0을, o1이 크면 양수를 리턴하는 compare() 메소드가 선언되어 있다.
@FunctionInterface
public interface Comparator<T>{
public int compare(T o1, T o2);
}
- Comparator<T>를 타겟 타입으로 하는 람다식은 다음과 같이 작성할 수 있다.
(o1,o2) ->{ ...; return int 값;} // o1이 o2 보다 작으면 음수, o1과 o2가 같으면 0, o1이 o2보다 크면 양수를 리턴한다.
예제
```java import java.util.function.BinaryOperator;
public class OperatorMinByMaxByExample {
public static void main(String[] args) {
BinaryOperator<Fruit> binaryOperator;
Fruit fruit;
binaryOperator = BinaryOperator.minBy(
(f1,f2)-> Integer.compare(f1.getPrice(),f2.getPrice())
);
// binaryOperator.apply()는 T와 T를 연산한 후 T를 리턴.
fruit = binaryOperator.apply(new Fruit("딸기",6000),
new Fruit("수박", 10000));
System.out.println("더 저렴한 과일: "+ fruit.getName());
binaryOperator = BinaryOperator.maxBy(
(f1,f2) -> Integer.compare(f1.getPrice(),f2.getPrice())
);
fruit = binaryOperator.apply(new Fruit("딸기",6000),
new Fruit("수박", 10000));
System.out.println("더 비싼 과일: " + fruit.getName());
} } ```