람다식과 스트림, 함수형 인터페이스(predicate, consumer, supplier, function, operator) 활용하기
1. 람다식
람다는 하나의 abstract 메서드 만 가진 인터페이스를 함수형 인터페이스라고 한다.
람다식을 지원하는 java.util.functon 패키지가 추가되었다.
1.1 람다 표현과 특성
1 | Runnable r = () -> System.out.println("Hello World"); | cs |
1 2 3 4 5 | public interface Comparator<T> { public int compare(T o1, T o2); } Comparator<Integer> comparator = (o1, o2) -> o2 - o1; | cs |
- 이름이 없는 익명이다.
- 메서드처럼 특정클래스에 종속되지 않는다.
- 람다식을 메서드의 인자로 전달하거나 변수값으로 저장할 수 있다.
- 간결하다.
2. 스트림
자바8은 스트림과 람다를 활용하여 이전보다 간결하고 향상된 방법으로 collection을 처리한다.
stream은 순차(sequential), 병렬(parallel) 두 종류가 존재한다.
순차 stream은 싱글 쓰레드가 순차적으로 처리되며 병렬 stream은 fork/join 프레임워크로 구현된
메서드에 의해 병렬로 처리된다.
2.1 stream의 장단점
장점은 Laziness, Collection의 interation처리를 자바에게 맡겨 둠으로써 jvm이 최적화할수 있는 기회를 제공한다.
단점은 재사용 불가하다. 한번 사용된 stream은 재사용이 불가능하여 필요에 따라 새롭게 만들어야 한다.
3. 함수형 인터페이스 활용하기
3.1 java.util.function 패키지의 함수형 인터페이스
함수형 인터페이스 | Descriptor | Method명 |
Predicate<T> | T -> boolean | test() |
Consumer<T> | T -> void | accept() |
Supplier<T> | () -> T | accept() |
Function<T,R> | T -> R | apply() |
UnaryOperator<T> | T -> T | identity() |
위에 함수형 인터페이스를 활용해서 예제를 작성해보겠다.
3.2 predicate와 consumer 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package lamdaTest; public class Grape { private int weight = 0; private String color = ""; public Grape(int weight, String color){ this.weight = weight; this.color = color; } public Integer getWeight() { return weight; } public void setWeight(Integer weight) { this.weight = weight; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String toString() { return "Grape{" + "color='" + color + '\'' + ", weight=" + weight + '}'; } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | package lamdaTest; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Consumer; import java.util.function.Predicate; public class PredicateAndConsumer { public static void main(String... args) { // 청포도, 보라포도, 적포도 List<Grape> inventory = Arrays.asList(new Grape(70, "green"), new Grape(90, "purple"), new Grape(120, "red")); // 1. Predicate 인터페이스 사용 System.out.println("----- 1. Predicate 인터페이스 사용 -----"); filter(inventory, grape -> grape.getWeight() >=100) .forEach(System.out::println); // result : Grape{color='red', weight=120} // 2. Consumer 인터페이스 사용 System.out.println("----- 2. Consumer 인터페이스 사용 -----"); printGrapeInfo(inventory, grape -> System.out.println(grape)); // result : All List print } public static void printGrapeInfo(List<Grape> inventory, Consumer<Grape> consumer) { for (Grape grape : inventory) { consumer.accept(grape); } } public static List<Grape> filter(List<Grape> inventory, Predicate<Grape> p) { List<Grape> result = new ArrayList<>(); for (Grape grape : inventory) { if (p.test(grape)) { result.add(grape); } } return result; } } | cs |
3.3 supplier 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | package lamdaTest; import java.util.function.Supplier; class Animal { public void eat() { System.out.println("Eating something"); } } class Cat extends Animal { @Override public void eat() { System.out.println("Eating fish"); } } public class SupplierTest { static void eatSomething(Supplier<? extends Animal> supplier) { Animal animal = supplier.get(); animal.eat(); } public static void main(String[] args) { // Using Lambda expression eatSomething(() -> new Animal()); eatSomething(() -> new Cat()); // Using Method Reference eatSomething(Animal::new); eatSomething(Cat::new); } } | cs |
3.4 function 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | package lamdaTest; import java.util.ArrayList; import java.util.List; import java.util.function.Function; public class FunctionTest { // Function 인터페이스 public static List<String> getColorList(List<Grape> inventory, Function<Grape, String> function) { List<String> colorList = new ArrayList<String>(); for (Grape grape : inventory) { colorList.add(function.apply(grape)); } return colorList; } public static void main(String[] args) { List<Grape> grapeList = new ArrayList<>(); grapeList.add(new Grape(150, "green")); grapeList.add(new Grape(200, "purple")); grapeList.add(new Grape(200, "red")); grapeList.add(new Grape(150, "red")); // 1. 익명 클래스 사용 System.out.println("----- 1. 익명 클래스 사용 -----"); getColorList(grapeList, new Function<Grape, String>() { @Override public String apply(Grape grape) { // TODO Auto-generated method stub return grape.getColor(); } }).forEach(System.out::println); // 2. 람다식 사용 System.out.println("----- 2. 람다식 사용 -----"); getColorList(grapeList, grape -> grape.getColor()).forEach(System.out::println); // 3. 메소드 레퍼런스 사용 System.out.println("----- 3. 메소드 레퍼런스 사용 -----"); getColorList(grapeList, Grape::getColor).forEach(System.out::println); } public static class Grape { private Integer weight = 0; private String color = ""; public Grape(Integer weight, String color) { this.weight = weight; this.color = color; } public Integer getWeight() { return weight; } public void setWeight(Integer weight) { this.weight = weight; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public String toString() { return "Grape{" + "color='" + color + '\'' + ", weight=" + weight + '}'; } } } | cs |
3.5 operator 예제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | package lamdaTest; import java.util.function.IntBinaryOperator; public class OperatorTest { private static int[] valueList = { 10, 20, 30 }; // IntBinaryOperator 사용 public static int maxOrMin(IntBinaryOperator operator) { int result = valueList[0]; for (int value : valueList) { result = operator.applyAsInt(result, value); } return result; } public static void main(String[] args) { // 최대값 얻기 int maxValue = maxOrMin((n1,n2) ->{ if(n1>=n2) return n1; else return n2; }); System.out.println(maxValue); // 최소값 얻기 int minValue = maxOrMin((n1,n2) ->{ if(n1<=n2) return n1; else return n2; }); System.out.println(minValue); } } | cs |
정리
1. 람다식은 익명클래스를 간결하게 표현할수 있다.
2. 함수형 인터페이스는 추상 메서드 하나만 정의된 인터페이스이다.
3. 메서드 레퍼런스를 이용하면 기존의 메서드 구현을 재사용 가능하다.
4. Comparator, Predicate, Function 같은 함수형 인터페이스는 람다식을 조합할수 이는 다양한 디폴트 메소드를 제공한다.
소스코드 첨부 :