Language/Java

Java - 람다식

둉이 2021. 7. 18. 20:17

 

람다식

: 익명 함수를 생성하기 위한 식, 함수에 가까움

  매개변수가 하나일 경우 소괄호 생략 가능, 람다식 내 코드가 한 줄일 경우 중괄호 생략 가능

  return문만 있을 때는 중괄호 생략 불가

// (타입 매개변수1, 매개변수2, ...) -> { ... }
// 혹은

// (매개변수1, 매개변수2, ...) -> { ... }

// 람다식 예시
(int a) -> { sout(a); };

- 람다식에서의 메소드 매개변수 또는 로컬 변수는 final 특성을 가짐

  : 수정 불가

 

 

함수적 인터페이스(@FunctionalInterface)

: 하나의 추상 메소드가 선언된 인터페이스를 람다식을 이용하여 구현 객체를 생성하고자 할 때 사용

인터페이스 상단에 @FunctionalInterface 어노테이션을 붙여서 사용

// 인터페이스 선언
@FunctionalInterface
public interface MyFunctionalInterface {
   public void method();
}

// 함수적 인터페이스 사용
MyFunctionalInterface fi = () -> { ... };

 

 

람다식 내에서 로컬 변수 사용

- 람다식 내에서 this 사용시 람다식 내부를 의미

- 람다식 바깥 객체를 참조하려면 클래스명.this.객체명으로 접근

 

 

표준 API의 함수적 인터페이스

: 자바에서 제공되는 표준 API 중, 하나의 추상 메소드를 가지는 인터페이스들은 모두 람다식을 이용하여 익명 구현 객체로 표현 가능

 ex) Runnable 

 

- 예시

public static void main(String[] args) {
	Runnable runnable = () -> {
        for(int i = 0; i < 10; i++) {
            System.out.println(i);
        }
    };
    Thread thread = new Thread(runnable);
    thread.start();
    
    // 또는
    
    Thread thread2 = new Thread(() -> {
        for(int i = 0; i < 10; i++) {
            System.out.println(i);
        }
	});
}

 

 

함수적 인터페이스 표준 패키지

종류 특징
Consumer 매개값은 있고 리턴값은 없음
Supplier 매개값은 없고 리턴값은 있음
Function 매개값은 있고 리턴값도 있음
주로 매개값을 리턴값으로 매핑(타입 변환)
Operator 매개값은 있고 리턴값도 있음
주로 매개값을 연산하고 결과를 리턴
Predicate 매개값은 있고 리턴값은 boolean 타입
매개값을 조사하여 true or false 반환

 

1. Consumer 함수적 인터페이스

: 리턴값이 없는 accept() 메소드를 가짐 → 해당 메소드로 Consumer 실행

// consumer 예시
Consumer<String> consumer = t -> { ... };
consumer.accept("java");

BiConsumer<String> consumer2 = (t, u) -> { ... };
DoubleConsumer<String> consumer3 = d -> { ... };
ObjIntConsumer<String> consumer4 = (t, i) -> { ... };

 

2. Supplier 함수적 인터페이스

: 매개변수가 없고 리턴값이 있는 getXXX() 메소드를 가짐 → get(), getAsInt() 등

// supplier 예시
Supplier<String> supplier = () -> { ... return ""; };
sout(supplier.get());

IntSupplier<String> supplier2 = () -> { ... return 1; };
sout(supplier2.getAsInt());

 

3. Function 함수적 인터페이스

: 매개값과 리턴값이 있는 applyXXX() 메소드를 가짐

// function 예시
private static List<Student> list = Arrays.asList(
   new Student("홍길동", 90, 96),
   new Student("신용권", 95, 93)
);

public static double avg(ToIntFunction<Student> function) {
   int sum = 0;
   for(Student student : list) {
      sum += function.applyAsInt(student);
   }
   double avg = (double) sum / list.size();
   return avg;
}

public static void main(String[] args) {
   double englishAvg = avg(s -> s.getEnglishScore());
   System.out.println("영어 평균 점수: " + englishAvg);
   double mathAvg = avg(s -> s.getMathScore());
   System.out.println("수학 평균 점수: " + mathAvg);
}

 

4. Operator 함수적 인터페이스

: Function과 동일하지만 Function과는 다르게 매개값을 리턴값으로 매핑하는 역할보다는 매개값을 이용하여 연산을 수행하여 그 값을 동일한 타입으로 리턴applyXXX() 메소드를 가짐

 

5. Predicate 함수적 인터페이스

: 매개 변수와 boolean 리턴값이 있는 testXXX() 메소드를 가짐, true/false 리턴

 Predicate, BiPredicate, DoublePredicate, IntPredicate, LongPredicate 등이 있음

// predicate 예시
Predicate<Student> predicate = t -> { return t.getSex().equals("남자"); }
// 또는
Predicate<Student> predicate = t -> t.getSex().equals("남자");

 

 

andThen()과 compose() 디폴트 메소드

: 함수적 인터페이스가 가지고 있는 디폴트 메소드

 두 개의 함수적 인터페이스를 연결하고, 첫 번째 처리 결과를 두 번째 매개값으로 제공

 

1. andThen()

: 인터페이스 A부터 처리하고 결과를 인터페이스 B의 매개값으로 제공 → 인터페이스 B의 최종 결과 리턴

  Consumer, Function, Operator 함수적 인터페이스에서 사용 가능

// andThen 예시

// 1. Consumer는 리턴 결과를 제공하지 않으므로 순서만 지정
Consumer<Member> consumerAB = consumerA.andThen(consumerB);

// 2. Function이나 Operator는 결과를 제공하므로 객체 형식 유의
Function<Member, Address> functionA;
Function<Address, String> functionB;
...
Function<Member, String> functionAB = functionA.andThen(functionB); 
Function<Member, String> functionAB = functionB.compose(functionA);   // 같음

 

2. compose()

: 인터페이스 B부터 처리하고 결과를 인터페이스 A의 매개값으로 제공 → 인터페이스 A의 최종 결과 리턴

  Function(BiFunction 불가), Operator(Binary 불가), 함수적 인터페이스에서 사용 가능

 

 

and(), or(), negate() 디폴트 메소드와 isEqual() 정적 메소드

: Predicate 함수적 인터페이스에서 사용 가능

 논리 연산자 &&, ||, !와 대응

// 1. and
IntPredicate predicateAB = predicateA.and(predicateB);

// 2. or
IntPredicate predicateAB = predicateA.or(predicateB);

// 3. negate
IntPredicate predicateAB = predicateA.negate();

// 4. isEqual
Predicate<String> predicate = Predicate.isEqual(null);
System.out.println(predicate.test(null));

 

 

minBy, maxBy 정적 메소드

: BinaryOperator에서 사용 가능

 매개값으로 제공되는 Comparator를 이용하여 최대 T와 최소 T를 얻는 BinaryOperator<T> 리턴

// 예시
BinaryOperator<Fruit> binaryOperator = BinaryOperator.minBy( (f1, f2) -> Integer.compare(f1.price, f2.price) );
Fruit friut = binaryOperator.apply(new Fruit("딸기", 6000), new Fruit("수박", 10000)); System.out.println(friut.name);

 

 

메소드 참조

메소드를 참조하여 매개변수의 정보 및 리턴 타입을 알아내어 람다식에서 불필요한 매개변수를 제거

 

- 정적 메소드와 인스턴스 메소드 참조

  : 클래스 이름 뒤에 :: 기호를 붙임

   정적 메소드는  클래스::메소드, 인스턴스 메소드는 참조변수::메소드 형태로 사용

// 정적 메소드와 인스턴스 메소드 참조 예시
IntBinaryOperator operator = (x, y) -> Calculator.staticMethod(x, y);
// 또는
IntBinaryOperator operator = Calculator::staticMethod;

 

- 매개변수의 메소드 참조

  : 정적 메소드와 같이  클래스::메소드 형태로 사용

// 매개변수의 메소드 참조 예시
ToIntBiFunction<String, String> function = (a, b) -> a.compareToIgnoreCase(b);
// 또는
ToIntBiFunction<String, String> function = String::compareToIgnoreCase;

 

- 생성자 참조

  : 객체 생성을 의미

    클래스::new 형태로 사용

// 생성자 참조 예시
Function<String, Member> function1 = Member::new;
Member member1 = function1.apply("angel");

'Language > Java' 카테고리의 다른 글

Java - 제네릭  (0) 2021.07.07
Java - 멀티 스레드 정리  (0) 2021.07.02
Java - 기본 API 클래스 정리  (0) 2021.06.25
Java - 중첩 클래스와 중첩 인터페이스, 예외 처리 복습  (0) 2021.06.25
Java - 인터페이스 복습  (0) 2021.06.24