티스토리 뷰

1. 함수형 인터페이스

2. 자바에서 제공하는 함수형 인터페이스

3. 람다 표현식

4. 메소드 참조

 

 

1. 함수형 인터페이스

  • 추상메소드를 하나만 가지고 있는 인터페이스
  • @FuncationInterface 어노테이션을 가지고 있는 인터페이스
// functional interface 를 annotation으로 정의 가능
// 선언 규칙위반일 때 컴파일 오류 발생 : 좀 더 견고하게 관리할 수 있다.
@FunctionalInterface
public interface FunctionalInterface01 {
    // 추상 메소드를 하나만 가지고 있는 인터페이스    
    // abstract 생략 가능
    void doIt();
	
    // static, default 메소드 정의 가능(java 8 에 추가된 기능)
    
    // public 생략 가능
    static void printName() {
        System.out.println("mandy");
    }

    default void printAge() {
        System.out.println("20");
    }
}

 

2. 자바에서 제공하는 함수형 인터페이스

1) Function : <parameter type, return type>

R apply(T t) : 파라미터 O, 리턴 X

  > 클래스로 선언 

import java.util.function.Function;

public class Plus5 implements Function<Integer, Integer> {

    @Override
    public Integer apply(Integer integer) {
        return integer+5;
    }
}

// main 메소드
Plus5 plus5 = new Plus5();
System.out.println(plus5.apply(2)); // 7

  > 람다 표현식으로 선언

Function<Integer, Integer> plus10 = number -> number + 10;
Function<Integer, Integer> muliply2 = number -> number * 2;
//UnaryOperator<Integer> muliply2 = number -> number + 10; // 인자값과 리턴값이 같은 경우 사용가능

// 함수 조합
// plus10.compose(muliply2) : muliply2 > plus10
// plus10.andThen(muliply2) : plus10 > muliply2
System.out.println(plus10.compose(muliply2).apply(2)); // 14
System.out.println(plus10.andThen(muliply2).apply(2)); // 24

 

2) Consumer : <parameter type>

void accept(T t) : 파라미터 O, 리턴 X

Consumer<Integer> printT = number -> System.out.println(number);
printT.accept(10); // 10

 

3) Supplier : <return type>

T get() : 파라미터 X, 리턴 O

Supplier<String> getName = () -> "mandy";
System.out.println(getName.get()); // mandy

 

4) Predicate : <parameter type> return boolean

boolean test(T t) : 파라미터 O,, 리턴 O(타입 : boolean)

Predicate<String> startsWith = (name) -> "mandykr".startsWith(name);
Predicate<String> endsWith = (name) -> "mandykr".endsWith(name);
Predicate<String> startsOrEndsWith = startsWith.or(endsWith);

System.out.println(startsOrEndsWith.test("mandy")); // true

 

추가 인터페이스 확인 : java.lang.function 패키지

 

3. 람다 표현식

쉐도윙 : 외부 스콥의 변수와 로컬 변수의 이름이 같은 경우 외부 변수가 로컬 변수에 가려짐.

private void run() {
    // effective final : 사실상 final인 경우(재정의가 발생하지 않는 경우) final 키워드 생략가능
    int baseNumber = 10;

    // 1. 로컬 클래스
    class localClass {
        void printBaseNumber(int baseNumber) { // 쉐도잉
            System.out.println(baseNumber);
        }
    }

    // 2. 익명 클래스
    IntConsumer intConsumer = new IntConsumer() {
        @Override
        public void accept(int baseNumber) { // 쉐도잉
            System.out.println(baseNumber);
        }
    };

    // 3. 람다
    // 익명 클래스는 새로 스콥을 만들지만, 람다는 람다를 감싸고 있는 스콥과 같다.
    // 쉐도윙이 일어나지 않고 컴파일 에러 발생(baseNumber가 이미 정의되어 있음)
    //IntConsumer lambdaIntConsumer = (baseNumber) -> System.out.println(baseNumber);

    // > 로컬변수 캡쳐
    // final 이거나 effective final 인 경우에만 참조 가능
    // 그렇지 않은 경우 컴파일 에러(concurrency)
    IntConsumer lambdaIntConsumer = (number) -> System.out.println(baseNumber);
    
    // effective final 이 깨지는 경우 람다에서 호출할 수 없음(컴파일 에러)
    // baseNumber++;
}

 

4. 메소드 참조

FunctionalInterface interface = 추상 메소드의 body;

public class Greeting {
    private String name;

    public Greeting() {
    }

    public Greeting(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public String hello(String name) {
        return "hello" + name;
    }

    public static String hi(String name) {
        return "hi" + name;
    }
}

 

1) static 메소드 참조

UnaryOperator<T> extends Function<T, T> : 입력값과 리턴값이 같은 Function인 경우 사용

UnaryOperator<String> hi = Greeting::hi;
System.out.println(hi.apply("mandy"));

// 익명 클래스로 변환해 생각해보면 ...
UnaryOperator<String> hi = new UnaryOperator<String>() {
  @Override
  public String apply(String name) {
  	return "hi" + name;
  }
};

 

2) 인스턴스 메소드 참조

Greeting greeting = new Greeting();
UnaryOperator<String> hello = greeting::hello;
System.out.println(hello.apply("mandy"));

 

3) 임의 객체의 인스턴스 메소드 참조

String[] names = {"mandy", "keesun", "toby"};

// 익명클래스로 표현
Arrays.sort(names, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return 0;
    }
});

// 람다로 표현
Arrays.sort(names, (o1, o2) -> 0);

// Comparator 객체(CASE_INSENSITIVE_ORDER)의 인스턴스 메소드 참조 사용
Arrays.sort(names, String::compareToIgnoreCase);

// String class
public static final Comparator<String> CASE_INSENSITIVE_ORDER
                                 = new CaseInsensitiveComparator();

public int compareToIgnoreCase(String str) {
    return CASE_INSENSITIVE_ORDER.compare(this, str);
}

 

4) 생성자 참조(default)

Greeting 타입을 리턴하는 default 생성자

Supplier<Greeting> greetingSupplier = Greeting::new;
System.out.println(greetingSupplier.get().hello("mandy"));

 

5) 생성자 참조(parameter)

String 타입을 인자로 받아 Greeting 타입을 리턴하는 생성자

Function<String, Greeting> greetingFunction = Greeting::new;
Greeting mandy = greetingFunction.apply("mandy");
System.out.println(mandy.getName());

 

 

 

 

출처

https://www.inflearn.com/course/the-java-java8 더 자바, Java 8(백기선)

728x90