First Class Citizon는 아래와 같은 속성들을 모두 만족해야 한다.
Java에 method는 위 조건 모두를 만족하지 않음으로 Java에서 method는 일급객체가 아니고 때문에 java는 함수형 프로그래밍 언어가 아니다
Java8에서는 함수를 일급객체처럼 다룰 수 있게 함수형 인터페이스를 제공한다. 함수형 인터페이스란 단 하나의 추상 메소드만 가지는 인터페이스를 의미한다.(static함수는 여러 개 가질 수 있다.)
함수형 인터페이스를 이용함으로서 객체를 선언하고 오버라이딩하고... 이런 노이즈들을 없애고 단순히 람다식 함수를 통해 코드를 간결하게 작성 할 수 있다.
계산기 if - else 문없이 구현하기
@FunctionalInterface
interface Calculation {
int calculate(final int num1, final int num2);
}
class FpCalculatorService {
public int calculate(final Calculation calculation, final int num1, final int num2) {
return calculation.calculate(num1, num2);
}
}
final FpCalculatorService fpCalculatorService = new FpCalculatorService();
final Calculation addition = (i1, i2) -> i1 + i2;
System.out.println("additon: " + fpCalculatorService.calculate(addition, 11, 4));
System.out.println("subtraction: " + fpCalculatorService.calculate((i1, i2) -> i1 - i2, 11, 1));
함수형 인터페이스를 통해 객체를 만들어서 overriding 할 필요없이 람다식만으로 파라미터로 넘겨준다.
만약 함수형 인터페이스를 사용하지 않을 경우 new Calcautioan () { @Override ~~} 와 같이 파라미터로 넣어 주어야 한다.
Function
Function<T, R> 형태로 T는 파라미터 타입, R은 리턴타입이다. 추상메소드 apply() 를 가진다.
public class FunctionExamples {
private static void functionExamples() {
final Function<String, Integer> toInt = value -> Integer.parseInt(value);
final Integer number = toInt.apply("100");
System.out.println(number);
final Function<Integer, Integer> identity = Function.identity(); // Identity Function
final Function<Integer, Integer> identity2 = t -> t; // Identity Function
System.out.println(identity.apply(999));
System.out.println(identity2.apply(999));
}
public static void main(final String[] args) {
functionExamples();
}
Predicate
Predicate<T, Boolean> 형태로, boolean을 리턴하는 함수형 인터페이스다. 추상메소드 test() 를 가진다.
public class PredicateExamples {
private static void predicateExamples() {
final Predicate<Integer> isPositive = i -> i > 0;
System.out.println(isPositive.test(1));
System.out.println(isPositive.test(0));
System.out.println(isPositive.test(-1));
final List<Integer> numbers = Arrays.asList(-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5);
final List<Integer> positiveNumbers = new ArrayList<>();
for (final Integer num : numbers) {
if (isPositive.test(num)) {
positiveNumbers.add(num);
}
}
System.out.println("positive integers: " + positiveNumbers);
final Predicate<Integer> lessThan3 = i -> i < 3;
final List<Integer> numbersLessThan3 = new ArrayList<>();
for (final Integer num : numbers) {
if (lessThan3.test(num)) {
numbersLessThan3.add(num);
}
}
System.out.println("less than 3: " + numbersLessThan3);
System.out.println("positive integers: " + filter(numbers, isPositive));
System.out.println("less than 3: " + filter(numbers, lessThan3));
}
// Steam API에서는 보다 간단하게 가능
private static <T> List<T> filter(final List<T> list, final Predicate<T> filter) {
final List<T> result = new ArrayList<>();
for (final T input : list) {
if (filter.test(input)) {
result.add(input);
}
}
return result;
}
public static void main(final String[] args) {
predicateExamples();
}
}
Consumer
Consumer는 < T > 형태로 리턴이 없는 형태이다. 추상메소드 accept() 를 가진다.
public class ConsumerExamples {
private static void consumerExamples() {
final Consumer<String> print = value -> System.out.println(value);
print.accept("Hello");
final Consumer<String> greetings = value -> System.out.println("Hello " + value);
greetings.accept("World");
greetings.accept("Kevin");
}
public static void main(final String[] args) {
consumerExamples();
}
}
Supplier
Supplier는 < R > 형태로 파라미터가 없는 형태이다. 추상메소드 get() 를 가진다.이를 이용해 Lazy Evalutioan이 가능하다.
package cc.kevinlee.modernjava.e05_supplier;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
/**
* @author Kevin Lee
* @since 2015-08-01
*/
public class SupplierExamples {
/**
* 계산 하는데 오래 걸리는 메소드를 시뮬레이션 해봤습니다. 항상 3초가 걸려요.
*
* @return 항상 "Kevin"만 리턴
*/
private static String getVeryExpensiveValue() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Kevin";
}
private static void callingExpensiveMethodWithoutSupplier() {
System.out.println("SupplierExamples.callingExpensiveMethodWithoutSupplier()");
final long start = System.currentTimeMillis();
printIfValidIndex(0, getVeryExpensiveValue());
printIfValidIndex(-1, getVeryExpensiveValue());
printIfValidIndex(-2, getVeryExpensiveValue());
System.out.println("It took " + ((System.currentTimeMillis() - start) / 1000) + " seconds.");
}
private static void printIfValidIndex(final int number, final String value) {
if (number >= 0) {
System.out.println("The value is " + value + ".");
} else {
System.out.println("Invalid");
}
}
private static void callingExpensiveMethodWithSupplier() {
System.out.println("SupplierExamples.callingExpensiveMethodWithSupplier()");
final long start = System.currentTimeMillis();
printIfValidIndex(0, () -> getVeryExpensiveValue());
printIfValidIndex(-1, () -> getVeryExpensiveValue());
printIfValidIndex(-2, () -> getVeryExpensiveValue());
System.out.println("It took " + ((System.currentTimeMillis() - start) / 1000) + " seconds.");
}
private static void printIfValidIndex(final int number, final Supplier<String> valueSupplier) {
if (number >= 0) {
System.out.println("The value is " + valueSupplier.get() + ".");
} else {
System.out.println("Invalid");
}
}
private static void supplierExamples() {
System.out.println("SupplierExamples.supplierExamples()");
final Supplier<String> helloSupplier = () -> "Hello ";
System.out.println(helloSupplier.get() + "world");
}
public static void main(final String[] args) {
supplierExamples();
System.out.println("\nSupplier 사용하지 않고 시간 오래 걸리는 메소드 호출");
callingExpensiveMethodWithoutSupplier();
System.out.println("\nSupplier 사용해서 시간 오래 걸리는 메소드 호출");
callingExpensiveMethodWithSupplier();
}
}