-
[Java] 람다식 - 표준 API의 함수적 인터페이스1CSE/Java 2015. 9. 20. 16:13
람다식은 여러 절로 구성되어 있습니다.
표준 API의 함수적 인터페이스
자바에서 제공되는 표준 API에서 한 개의 추상 메소드를 가지는 인터페이스들은 모두 람다식을 이용해서 익명 구현 객체로 표현이 가능합니다. 예를 들어 스레드의 작업을 정의하는 Runnable 인터페이스는 매개 변수와 리턴값이 없는 run() 메소드만 존재하기 때문에 다음과 같이 람다식을 이용해서 Runnable 인스턴스를 생성시킬 수 있습니다.
* RunnableExam.java
123456789101112131415161718package lambda;public class RunnableExam {public static void main(String[] args) {Runnable runnable = () -> {for (int i = 0; i < 10; i++) {System.out.println(i);}};Thread thread = new Thread(runnable);thread.start();}}cs Thread 생성자를 호출할 때 다음과 같이 람다식을 매개값에 대입해도 됩니다.
12345Thread thread = new Thread( () -> {for (int i = 0; i < 10; i++) {System.out.println(i);}});cs 자바 8 부터는 빈번하게 사용되는 함수적 인터페이스(Functional Interface)는 java.util.function 표준 API 패키지로 제공합니다. 이 패키지에서 제공하는 함수적 인터페이스의 목적은 메소드 또는 생성자의 매개 타입으로 사용되어 람다식을 대입할 수 있도록 하기 위해서 입니다. java.util.function 패키지의 함수적 인터페이스는 크게 Consumer, Supplier, Function, Operator, Predicate로 구분됩니다.
Consumer 함수적 인터페이스
Consumer 함수적 인터페이스의 특징은 리턴값이 없는 accept() 메소드를 가지고 있습니다. accept() 메소드는 단지 매개값을 소비하는 역할만 합니다. 여기서 소비한다는 말은 사용만 할 뿐 리턴값이 없다는 뜻입니다.
매개 변수의 타입과 수에 따라서 아래와 같은 Consumer들이 있습니다.
Consumer<T> 인터페이스를 타켓 타입으로 하는 람다식은 다음과 같이 작성할 수 있습니다. accept() 메소드는 매개값으로 T 객체 하나를 가지므로 람다식도 한 개의 매개 변수를 사용합니다. 타입 파라미터 T에 String이 대입되었기 때문에 람다식의 t 매개변수 타입은 String이 됩니다.
1Consumer<String> consumer = t -> { t를 소비하는 실행문; };cs * ConsumerExam.java
12345678910111213141516171819202122232425package lambda;import java.util.function.BiConsumer;import java.util.function.Consumer;import java.util.function.DoubleConsumer;import java.util.function.ObjIntConsumer;public class ConsumerExam {public static void main(String[] args) {Consumer<String> consumer = t -> System.out.println(t + "8");consumer.accept("자바");BiConsumer<String, String> biConsumer = (t, u) -> System.out.println(t + u);biConsumer.accept("자바", "8");DoubleConsumer doubleConsumer = d -> System.out.println("자바" + d);doubleConsumer.accept(8.0);ObjIntConsumer<String> objIntConsumer = (t, i) -> System.out.println(t + i);objIntConsumer.accept("자바", 8);}}cs Supplier 함수적 인터페이스
Supplier 함수적 인터페이스의 특징은 매개 변수가 없고 리턴값이 있는 getXXX() 메소드를 가지고 있습니다. 이 메소드들은 실행 후 호출한 곳으로 데이터를 리턴(공급)하는 역할을 합니다.
리턴 타입에 따라서 아래와 같은 Supplier 함수적 인터페이스들이 있습니다.
Supplier<T> 인터페이스를 타겟 타입으로 하는 람다식은 다음과 같이 작성할 수 있습니다. getAsInt() 메소드가 매개값을 가지지 않으므로 람다식도 ()를 사용합니다. 람다식의 중괄호 {}는 반드시 int 값을 리턴하도록 해야 합니다.
12IntSupplier supplier = () -> {...; return int_value; }cs 다음 예제는 주사위의 숫자를 랜덤하게 공급하는 IntSupplier 인터페이스를 타겟 타입으로 하는 람다식입니다.
* SupplierExam.java
1234567891011121314151617181920package lambda;import java.util.function.IntSupplier;public class SupplierExam {public static void main(String[] args) {IntSupplier intSupplier = () -> {int num = (int) (Math.random() * 6) + 1;return num;};int num = intSupplier.getAsInt();System.out.println("눈의 수 : " + num);}}cs Function 함수적 인터페이스
Function 함수적 인터페이스의 특징은 매개값과 리턴값이 있는 applyXXX() 메소드를 가지고 있습니다. 이 메소드들은 매개값을 리턴값으로 매핑하는 역할을 합니다. 매가 변수 타입과 리턴 타입에 따라서 아래와 같은 Function 함수적 인터페이스가 있습니다.
Function<T, R> 인터페이스를 타겟 타입으로 하는 람다식은 다음과 같이 작성할 수 있습니다. apply() 메소드는 매개값으로 T 객체 하나를 가지므로 람다식도 한 개의 매개 변수를 사용합니다. 그리고 apply( )메소드의 리턴 타입이 R이므로 람다식 중괄호 {}의 리턴값은 R 객체가 됩니다. T가 Student 타입이고 R이 String 타입이므로 t 매개 변수 타입은 Student가 되고, 람다식의 중괄호 {}는 String을 리턴해야 합니다. t.getName()은 Student 객체의 getName() 메소드를 호출해서 학생 이름을 얻습니다. return문만 있을 경우 중괄호 {}와 return 문은 생략할 수 있습니다. 다음 코드는 Student 객체를 학생 이름(String)으로 매핑하는 것입니다.
123456Function<Student, String> function = t -> { return t.getName(); };orFunction<Student, String> function = t -> t.getName();cs 다음 예제는 List에 저장된 학생 객체를 하나씩 꺼내서 이름과 점수를 출력합니다.
* Student.java
1234567891011121314151617181920212223242526272829303132333435363738394041package lambda;public class Student {private String name;private int englishScore;private int mathScore;public Student(String name, int englishScore, int mathScore) {super();this.name = name;this.englishScore = englishScore;this.mathScore = mathScore;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getEnglishScore() {return englishScore;}public void setEnglishScore(int englishScore) {this.englishScore = englishScore;}public int getMathScore() {return mathScore;}public void setMathScore(int mathScore) {this.mathScore = mathScore;}}cs * FunctionExam1.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647package lambda;import java.util.Arrays;import java.util.List;import java.util.function.Function;import java.util.function.ToIntFunction;public class FunctionExam1 {private static List<Student> list = Arrays.asList(new Student("Jackie", 90, 65),new Student("Jolie", 100, 100));public static void printString(Function<Student, String> function) {for (Student std : list) {System.out.print(function.apply(std) + " ");}System.out.println();}public static void printInt(ToIntFunction<Student> function) {for (Student std : list) {System.out.print(function.applyAsInt(std) + " ");}System.out.println();}public static void main(String[] args) {System.out.println("학생 이름: ");printString( t -> t.getName() );System.out.println("영어 점수: ");printInt( t -> t.getEnglishScore() );System.out.println("수학 점수: ");printInt( t -> t.getMathScore() );}}cs 다음 예제는 List에 저장된 학생 객체를 하나씩 꺼내어 영어 점수와 수학 점수의 평균값을 산출합니다.
* FunctionExam2.java
1234567891011121314151617181920212223242526272829303132333435363738package lambda;import java.util.Arrays;import java.util.List;import java.util.function.ToIntFunction;public class FunctionExam2 {private static List<Student> list = Arrays.asList(new Student("Jolie", 100, 89),new Student("Martin", 77, 94),new Student("Pierre", 49, 100),new Student("Paul", 80, 78));public static double avg(ToIntFunction<Student> function) {int sum = 0;for (Student std : list) {sum += function.applyAsInt(std);}double avg = (double) sum / list.size();return avg;}public static void main(String[] args) {double englishAvg = avg( s -> s.getEnglishScore() );System.out.println("영어 평균 점수: " + englishAvg);double mathAvg = avg( s -> s.getMathScore() );System.out.println("영어 평균 점수: " + mathAvg);}}cs Operator 함수적 인터페이스
Operator 함수적 인터페이스는 Function과 동일하게 매개 변수와 리턴값이 있는 applyXXX() 메소드를 가지고 있습니다. 하지만 이 메소드들은 매개값을 리턴값으로 매핑(타입 변환)하는 역할보다는 매개값을 이용해서 연산을 수행한 후 동일한 타입으로 리턴값을 제공하는 역할을 합니다. 매개 변수의 타입과 수에 따라서 아래와 같은 Operator 함수적 인터페이스들이 있습니다.
다음 예제는 int[] 배열에서 최대값과 최소값을 얻습니다. maxOrMin() 메소드는 IntBinaryOperator 매개 변수를 가지고 있습니다. 따라서 maxOrMin() 메소드를 호출할 때 람다식을 이용할 수 있습니다.
* OperatorExam.java
12345678910111213141516171819202122232425262728293031323334353637383940414243444546package lambda;import java.util.function.IntBinaryOperator;public class OperatorExam {private static int[] scores = {100, 92, 81, 78, 88, 96, 55, 94};public 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;elsereturn b;});System.out.println("최대 값 : " + max);int min = maxOrMin((a, b) -> {if (a <= b)return a;elsereturn b;});System.out.println("최소 값 : " + min);}}cs Predicate 함수적 인터페이스
Predicate 함수적 인터페이스는 매개 변수와 boolean 리턴값이 있는 testXXX() 메소드를 가지고 있습니다. 이 메소드들은 매개값을 조사해서 true 또는 false를 리턴하는 역할을 합니다. 매개 변수 타입과 수에 따라서 아래와 같은 Predicate 함수적 인터페이스들이 있습니다.
다음 예제는 List에 저장된 남자 또는 여자 학생들의 평균 점수를 출력하는 예제입니다.
* PredicateExam.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859package lambda;import java.util.Arrays;import java.util.List;import java.util.function.Predicate;public class PredicateExam {private static List<Student> list = Arrays.asList(new Student("Martin", 80, 90, "Male"),new Student("Jolie", 74, 88, "Female"),new Student("Sophie", 66, 100, "Female"),new Student("Pierre", 100, 78, "Male"),new Student("anne", 80, 90, "Female"),new Student("Paul", 42, 91, "Male"),new Student("cristianne", 99, 100, "Female"),new Student("Mcg", 100, 90, "Male"));public static double[] avg(Predicate<Student> predicate) {int count = 0, engSum = 0, mathSum = 0;for (Student std : list) {if (predicate.test(std)) {count++;engSum += std.getEnglishScore();mathSum += std.getMathScore();}}double avg[] = {((double) engSum / count), ((double) mathSum / count)};return avg;}public static void main(String[] args) {double maleAvg[] = avg( t -> t.getSex().equals("Male"));System.out.println("남자 평균 점수(영어, 수학)");for (double avg : maleAvg) {System.out.print(avg + " ");}System.out.println();double femaleAvg[] = avg( t -> t.getSex().equals("Female"));System.out.println("여자 평균 점수(영어, 수학)");for (double avg : femaleAvg) {System.out.print(avg + " ");}System.out.println();}}cs * Student.java
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950package lambda;public class Student {private String name;private int englishScore;private int mathScore;private String sex;public Student(String name, int englishScore, int mathScore, String sex) {super();this.name = name;this.englishScore = englishScore;this.mathScore = mathScore;this.sex = sex;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getEnglishScore() {return englishScore;}public void setEnglishScore(int englishScore) {this.englishScore = englishScore;}public int getMathScore() {return mathScore;}public void setMathScore(int mathScore) {this.mathScore = mathScore;}}cs * 이 포스트은 서적 '이것이 자바다' 를 참고하여 작성한 포스트입니다.
'CSE > Java' 카테고리의 다른 글
[Java] 멀티 스레드 (0) 2015.12.12 [Java] 람다식 - 메소드 참조 (0) 2015.09.23 [Java] 람다식 - 표준 API의 함수적 인터페이스2 (0) 2015.09.23 [Java] 람다식 - 클래스 멤버와 로컬 변수 사용 (0) 2015.09.20 [Java] 람다식 - 기본 문법, 타겟 타입과 함수적 인터페이스 (0) 2015.09.20 [Java] 람다식 - Intro (0) 2015.09.20