리루

Java8 함수형 인터페이스 본문

# Study/JAVA

Java8 함수형 인터페이스

뚱보리루 2020. 7. 4. 16:16

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