[JAVA] 람다식, 함수형 인터페이스

dejeong·2024년 9월 23일
1

JAVA

목록 보기
17/24
post-thumbnail

람다식이란

함수는 간단한 식으로 표현하는 방법, 익명함수를 생성하기 위한 식이고, 자바에서는 Java8부터 람다식을 객체로 취급할 수 있다.

람다식의 형태는 매개변수를 가진 코드 블록이지만, 런타임 시에는 익명 구현 객체를 생성한다.

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("익명 구현 객체의 메서드");
    }
};  // 익명 구현 객체
Runnable runnable1 = () -> System.out.println("익명 구현 객체의 메서드");

기본 문법

(타입 매개변수, ...) -> { 실행문; ...; }
// int 매개변수 a의 값을 콘솔에 출력
(int a) -> { System.out.println(a); }

// 매개 변수 타입은 런타임 시 대입되는 값에 따라 자동으로 인식
(a) -> { System.out.println(a); }

// 하나의 매개변수만 있을 때
a -> System.out.println(a);

// 매개 변수가 없을 
() -> System.out.println(a);

// 중괄호 안의 실행문을 실행하고 결과값을 리턴
(x, y) -> { return x + y; };

// 중괄호 안에 return 문만 있을 때
(x, y) -> x + y;

람다식으로 변경 예시

// 전
int max(int a, int b) {
    return a > b ? a : b;
}

// 변경 후
(a, b) -> a > b ? a : b
// 전
int print(String name, int i) {
    System.out.println(name + " = " + i);
}

// 변경 후
(name, i) -> System.out.println(name + " = " + i);
// 전
int square(int x) {
    return x * x;
}

// 변경 후
x -> x * x

어떻게 추론할까? 컴파일러가 함수형 인터페이스를 자동으로 변환해주기 때문에 각각의 값 안에 들어있는 추론이 가능해진다.


함수형 인터페이스

단 하나의 추상 메서드만 선언된 인터페이스

@FunctionalInterface
public interface MyFuntionalInterface {
    public void method();
    public void method2();    // 컴파일 오류 발생
}

@FunctionalInterface 어노테이션은 선택사항이다. 어노테이션이 없더라도 하나의 추상 메소드만 있다면 모두 함수형 인터페이스 이지만. 실수로 두 개 이상의 추상 메소드를 선언하는 것을 방지하고 싶다면 붙여주는 것이 좋다.

매개 변수와 리턴값이 없는 람다식

매개 변수와 리턴값이 없는 추상 메소드를 가진 함수형 인터페이스가 있을 때 해당 인터페이스를 타켓으로 갖는 람다식은 MyFunctionalInterface finter = () -> { ... } 와 같이 작성해야 하는데, 매개 변수가 없는 이유는 method()가 매개 변수를 갖지 않기 때문이다.

package chap12.custom;

@FunctionalInterface
public interface MyFunctionalInterface {
    void run();
}
package chap12;

import chap12.custom.MyFunctionalInterface;

public class MyFunctionalInterfaceExample {

  public static void main(String[] args) {
      MyFunctionalInterface inter = new MyFunctionalInterface(){
          @Override
          public void run(){
              System.out.println("Hello Lambda");
              System.out.println("Hello Lambda2");
              System.out.println("Hello Lambda3");
          }
      };
      inter.run();

      MyFunctionalInterface inter2 = () -> {
          System.out.println("Hello Lambda");
          System.out.println("Hello Lambda2");
          System.out.println("Hello Lambda3");
      };
      inter2.run();
}

매개 변수가 있는 람다식

매개 변수가 있고 리턴값이 없는 추상 메소드를 가진 함수형 인터페이스

package chap12.custom;

@FunctionalInterface
public interface MyFunctionalInterface2 {
    void method(int x);
}

람다식에서 매개변수가 한 개인 이유는 method()가 매개 변수를 하나만 가지기 때문이고, 람다식이 대입된 인터페이스 참조 변수는 method()를 호출할 수 있다.

 // 매개변수가 있는 람다식
MyFunctionalInterface2 inter3 = (x) -> System.out.println(x);
inter3.method(12);

리턴값이 있는 람다식

package chap12.custom;

@FunctionalInterface
public interface MyFunctionalInterface3 {
    int method(int a);
}
// 매개변수가 있는 람다식
MyFunctionalInterface2 inter3 = (x) -> System.out.println(x);
inter3.method(12);

// 매개변수와 리턴 타입이 있는 람다식(매개변수 1개)
MyFunctionalInterface3 inter4 = a -> a * 2;
int result = inter4.method(12);
System.out.println(result);
package chap12.custom;

public interface MyFunctionalInterface4 {
    int method(int a, int b);
}
 // 매개변수와 리턴 타입이 있는 람다식(매개변수 2개)
MyFunctionalInterface4 inter5 = (i, j) -> {
    System.out.println("매개변수가 2개인 method");
    return i * j;
};

int result2 = inter5.method(3,5);
System.out.println("result2: " + result2);

MyFunctionalInterface4 inter6 = (i, j) -> i + j;    // 익명 구현 객체
int result3 = inter6.method(1,5);

함수형 인터페이스의 장점

  • 재사용이 가능한 코드를 만들 수 있다.
  • 간결하고 직관적인 코드를 작성할 수 있다.
  • 불필요한 코드 작성이나 중복 코드를 줄여준다.

java.util.function 패키지

자주 사용되는 다양한 함수형 인터페이스를 제공하고, 같은 매개변수 타입과 리턴타입의 람다식을 참조 가능하다.

Runnable

매개변수와 리턴 값 모두 없는 경우

package java.lang;

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
Runnable r = () -> System.out.println("출력문 테스트");
r.run();    // "출력문 테스트" 

Supplier

매개변수는 없고, 리턴값(타입)이 있는 경우

package java.util.function;

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
Supplier<String> s = **() -> "리턴되는 값";**
String result = s.get();
System.out.println(result);   // "리턴되는 값"

실습

Supplier<String> supplier = () -> "str"; // 익명 구현 객체
String getStr = supplier.get();
System.out.println(getStr);

// Supplier<Integer>
IntSupplier intsupplier = () -> 123;
int getInt = intsupplier.getAsInt();
System.out.println(getInt);

Consumer

매개 변수는 있지만, 리턴 타입은 없는 경우

package java.util.function;

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}
Consumer<String> c = (a) -> System.out.println(a);
c.accept("consumer");

실습

Consumer<String> consumer = x -> System.out.println(x);
consumer.accept("Consumer 사용해서 입력값 출력하기");

Function<T, R>

하나의 매개변수를 받아서 하나의 결과를 리턴

package java.util.function;

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
Function<Integer, String> f = a -> String.valueOf(a);
Function<String, Integer> f2 = b -> {
    return Integer.valueOf(b) + 100;
};

실습

 // Function<Integer, String> function = x -> String.valueOf(x) + ":문자열";
Function<Integer, String> function = x -> x + ":문자열";
System.out.println(function.apply(123));

Function<String, Double> function2 = x -> Double.valueOf(x) * 1.2;
Double resultFunction = function2.apply("56.7"); // 문자열을 Double 타입으로 반환하고 1.2를 곱해줌 -> Double 타입으로 출력
System.out.println("String to Double : " + resultFunction);

Predicate

조건식을 표현하는 데 사용, 매개 변수는 하나, 리턴타입은 boolean을 가지는 함수형 인터페이스

package java.util.function;

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}
Predicate<String> isEmptyStr = s -> s.length()==0;
String str = "";
if (isEmptyStr.test(str)) { // if(s.length()==0)
		System.out.println("This is an empty String.")
}

// 스트림에서 filter메소드 내부에는 Predicate 타입이 들어감
List<Integer> list = Arrays.asList(1,2,3,4,5);
list.stream()
		.filter(x -> x%2==0)
		.collect(Collectors.toList()); // [2,4] 

실습

 // Predicate 구현 객체를 람다식으로 작성
// test 라는 가상 클래스 사용
Predicate<String> isEmpty = (x) -> x.length() == 0; // String :: isEmpty , 클래스명 :: 메서드명
System.out.println("빈값 체크 : " + isEmpty.test(""));
System.out.println("빈값 체크 : " + isEmpty.test("12345678"));
Predicate<Integer> predicate = x -> x % 2 == 0;
System.out.println("짝수 체크 : " + predicate.test(19));
profile
룰루

0개의 댓글