리루
Java8 함수형 인터페이스 본문
Functional Interface : Any interface with a SAM(Single Abstract Method) is a functional interface, and its implementation may be treated as lambda expressions.
- Java8에서는 Interface에 static method / default method가 사용가능하다. 이러한 추가적인 함수들이 있더라도 SAM이 하나만 존재한다면 Functional Interface라고 할 수 있다.
- `void do something();` 앞에는 `abstract`가 생략되어 있다고 볼 수있다.
- Abstract Method가 복수개가 되면 컴파일에러가 발생한다.(`@FunctionalInterface`가 있는 경우)
- `@FunctionalInterface`는 자바 standard library에 포함되어 있기 때문에 따로 import 되지 않는다.
package com.example.demok; @FunctionalInterface public interface Something { void doSomething(); static void doStatic(){ System.out.println("Static method"); } default void doDefault(){ System.out.println("Default method"); } }
package com.example.demok; public class RunSomething { public static void main(String[] args) { // Anonymous inner class Something something = new Something() { @Override public void doSomething() { System.out.println("Something"); } }; something.doSomething(); // Lambda expression Something newSomething = () -> System.out.println("New Something"); newSomething.doSomething(); // Lambda expression 2 Something multiSomething = () -> { System.out.println("New Something 1"); System.out.println("New Something 2"); }; multiSomething.doSomething(); } }
특징
1. 함수를 First class object로 사용 할 수 있다.
- Lambda expression 같은 형태를 리턴, 매개변수 등 Objcect 같이 사용할 수 있다.
2. 순수함수(Pure Function)
- Side Effect를 만들 수 없다. 함수 밖 변수 변경할 수 없다.
- 동일한 input이 들어가면 결과는 항상 같아야 한다.
- 상태가 없다.
3. 고차함수(Higher Order Function)
- 함수가 함수를 매개변수로 사용할 수 있고, 함수를 리턴할 수 있다.
자바가 기본으로 제공해주는 함수형 인터페이스들이 있다.
1. Function
/** * Represents a function that accepts one argument and produces a result. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #apply(Object)}. * * @param <T> the type of the input to the function * @param <R> the type of the result of the function * * @since 1.8 */ @FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); }
package com.example.demok; import java.util.function.Function; public class AppendQuestionMark implements Function<String, String> { @Override public String apply(String s) { return s.concat("!"); } }
package com.example.demok; import java.util.function.Function; public class RunSomething { public static void main(String[] args) { AppendQuestionMark appendQuestionMark = new AppendQuestionMark(); System.out.println(appendQuestionMark.apply("Let's Go Home")); Function<String, String> concatQuestionMark = (s) -> s.concat("!"); System.out.println(concatQuestionMark.apply("Let's Go Home")); } }
1-1. compose(), andThen()
- 다른 함수들과 조합해서 사용할 수 있는 함수 제공.
- compose 내부의 함수 먼저 수행 후, 외부함수 수행
@FunctionalInterface public interface Function<T, R> { /** * Returns a composed function that first applies the {@code before} * function to its input, and then applies this function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of input to the {@code before} function, and to the * composed function * @param before the function to apply before this function is applied * @return a composed function that first applies the {@code before} * function and then applies this function * @throws NullPointerException if before is null * * @see #andThen(Function) */ default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } /** * Returns a composed function that first applies this function to * its input, and then applies the {@code after} function to the result. * If evaluation of either function throws an exception, it is relayed to * the caller of the composed function. * * @param <V> the type of output of the {@code after} function, and of the * composed function * @param after the function to apply after this function is applied * @return a composed function that first applies this function and then * applies the {@code after} function * @throws NullPointerException if after is null * * @see #compose(Function) */ default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } }
package com.example.demok; import java.util.function.Function; public class RunSomething { public static void main(String[] args) { Function<String, String> concatQuestionMark = (s) -> s.concat("?"); Function<String, String> concatStarMakr = (s) -> s.concat("*"); Function<String, String> multiFunction = concatQuestionMark.compose(concatStarMakr); // Would you like something to drink*? System.out.println(multiFunction.apply("Would you like something to drink")); // Would you like something to drink?* System.out.println(concatQuestionMark.andThen(concatStarMakr).apply("Would you like something to drink")); } }
2. Consume
- 반환타입은 없고, 입력타입만 존재하는 함수형 인터페이스
/** * Represents an operation that accepts a single input argument and returns no * result. Unlike most other functional interfaces, {@code Consumer} is expected * to operate via side-effects. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #accept(Object)}. * * @param <T> the type of the input to the operation * * @since 1.8 */ @FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t); }
3. Supplier
- 입력타입은 없고 반환타입만 존재하는 함수형 인터페이스
/** * Represents a supplier of results. * * <p>There is no requirement that a new or distinct result be returned each * time the supplier is invoked. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #get()}. * * @param <T> the type of results supplied by this supplier * * @since 1.8 */ @FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
package com.example.demok; import java.util.function.Consumer; import java.util.function.Supplier; public class RunSomething { public static void main(String[] args) { // Hello World! Consumer<String> consumer = (s) -> System.out.println("Hello" + s); consumer.accept("World!"); // Hello World! Supplier<String> supplier = () -> "Hello World!"; System.out.println(supplier.get()); } }
4. Predicate
- 인자 하나를 받아서 true / false 를 반환해주는 함수형 인터페이스
/** * Represents a predicate (boolean-valued function) of one argument. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #test(Object)}. * * @param <T> the type of the input to the predicate * * @since 1.8 */ @FunctionalInterface public interface Predicate<T> { /** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t); }
package com.example.demok; import java.util.function.Predicate; public class RunSomething { public static void main(String[] args) { // True Predicate<Integer> predicate = (i) -> i < 10; System.out.println(predicate.test(5)); } }
5. UnaryOperator
- Function의 입출력 두 값의 형태가 동일 할 때 사용가능
package java.util.function; /** * Represents an operation on a single operand that produces a result of the * same type as its operand. This is a specialization of {@code Function} for * the case where the operand and result are of the same type. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #apply(Object)}. * * @param <T> the type of the operand and result of the operator * * @see Function * @since 1.8 */ @FunctionalInterface public interface UnaryOperator<T> extends Function<T, T> { /** * Returns a unary operator that always returns its input argument. * * @param <T> the type of the input and output of the operator * @return a unary operator that always returns its input argument */ static <T> UnaryOperator<T> identity() { return t -> t; } }
package com.example.demok; import java.util.function.UnaryOperator; public class RunSomething { public static void main(String[] args) { UnaryOperator<String> unaryOperator = (s) -> s + "world!"; System.out.println(unaryOperator.apply("Hello ")); } }
변수 캡쳐
- 로컬 클래스, 익명 클래스, 람다함수 에서는 외부 변수가 `final`인 경우에만 참조가 가능했다.
- Java8부터는 `final`이 명시적으로 붙어 있지 않아도 Effective fianl(`final`이 붙어 있지 않지만 실질적으로 변화가 없어 final처럼 사용되는 변수)는 사용가능하다.
- Effective final 변수가 어디선가 변경된다면 컴파일 에러가 발생한다.
- 로컬 클래스, 익명 클래스, 네스트 클래스는 각 클래스가 생성되면 해당 클래스에 새로은 scope이 생겨서 변수에 대한 쉐도윙이 일어난다.
- 람다는 사용되는 메서드와 scope가 같아서 쉐도윙이 되지 않는다.
package com.example.demok; import java.util.function.Consumer; public class RunSomething { public static void main(String[] args) { } public void run(){ int localValue = 1; // Inner Class class innerClass { int localValue = 2; void method(){ System.out.println(localValue); } } // Anonymous class Consumer<Integer> consumer = new Consumer<Integer>() { @Override public void accept(Integer localValue) { System.out.println(localValue); } }; // Lambda // Error : Variable 'localValue' is already defined in the scope Consumer<Integer> integerConsumer = (localValue) -> System.out.println(localValue); } }
'# Study > JAVA' 카테고리의 다른 글
Java8 - Optional (0) | 2020.07.05 |
---|---|
Steam (0) | 2020.07.05 |
[퍼온글]Volatile 변수 (0) | 2017.03.31 |
[퍼온글]Servlet이란? (0) | 2017.03.31 |