리루
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 |