CH14

yeon·2022년 12월 11일
0

이것이자바다

목록 보기
11/15

1. 람다식이란

함수적 프로그래밍

y=f(x) 형태의 함수로 구성된 프로그래밍 기법으로 데이터를 매개값으로 전달하고 결과를 받는 코드들로 구성되며 대용량 데이터의 처리, 이벤트 지향 프로그래밍에 적합한 기법

람다식

람다 계산법에서 사용된 식을 프로그래밍 언어에 접목해 익명 함수를 생성하기 위한 식으로 사용되며 코드가 매우 간결해지고 컬렉션 요소를 필터링 또는 매핑해서 쉽게 집계 가능
(타입 매개변수, ...) -> {실행문;...}

람다식은 함수적 인터페이스의 익명 구현 객체로 취급됨

Runnable runnable = new Runnable(){
	public void run(){...}
}


Runnable runnable = () -> {...};
/// run() 함수의 구현 객체가 됨

2. 람다식 기본 문법

(타입 매개변수, ...) -> {실행문; ...}
를 통해 함수적 스타일의 람다시 ㄱ장성 가능

  • 매개 타입은 런타입 시에 대입값에 따라 자동으로 인식하기 때문에 생략 가능 : (a) -> {System.out.println(a);}
  • 하나의 매개변수만 있을 경우에는 괄호() 생략 가능 : a -> {System.out.println(a);}
  • 하나의 실행문만 있다면 중괄호 {} 생략 가능 : a -> System.out.println(a)
  • 매개변수가 없다면 괄호() 생략 불가능 : () -> {System.out.println("hello");
  • 리턴값 있는 경우 return 문 사용 : (x, y) -> {return x+y;}
  • 중괄호{}에 있는 return 문만 있을 경우 중괄호 생략 가능 : (x, y) -> x+y

3. 타겟 타입과 함수적 인터페이스

타겟 타입

람다식이 대입되는 인터페이스로 익명 구현 객체 만들 인터페이스를 의미

함수적 인터페이스

람다식은 하나의 메소드를 정의하기 때문에 하나의 추상 메소드만 선언된 인터페이스만 타겟 타입이 될 수 있음
하나의 추상 메소드만 선언된 인터페이스를 함수적 인터페이스라고 하며 @FunctionalInterface 어노테이션을 통해 컴파일러가 체크하도록 함

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

@FunctionalInterface
public interface MyFunctionalInterface{
	public void method();
}

MyFunctionalInterface fi = () -> {...}
fi.method();

매개변수 있는 람다식

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

MyFunctionalInterface fi = (x) -> {...}
MyFunctionalInterface fi = x -> {...}

fi.method(5);

리턴값 있는 람다식

@FunctionalInterface
public interface MyFunctionalInterface{
	public int method(int x, y);
}

MyFunctionalInterface fi = (x, y) -> {...; return value;}

4. 클래스 멤버와 로컬 변수 사용

클래스의 멤버 사용

람다식 실행 블록에는 클래스 멤버인 필드, 메소드를 제약 없이 사용할 수 있기 때문에 람다식 실행 블록 내에서 this는 람다식을 실행한 객체의 참조

public class ThisExample{
	public int outterField = 10;
    
    class Inner{
    	int innerField = 20;
        
        void method(){
        	MyFunctionalInterface fi = () -> {
            	System.out.println("outterField : " + outterField);
                System.out.println("outterField : " + This.Example.this.outterField + "\n");
                System.out.println("innerField: " + innterField);
                System.out.println("innterField:" + this.innerField + "\n");
            };
            fi.method();
        }
    }
}

로컬 변수의 사용

람다식은 함수적 인터페이스의 익명 구현 객체를 생성하기 때문에 람다식에서 사용하는 외부 로컬 변수는 final 특성을 가짐

public class UsingLocalVariable{
	void method(int arg){
    	int localVar = 40;
        
        //arg = 31;
        //localVar = 41;
        
        MyFunctionalInterface fi = () -> {
        	System.out.println("arg: " + arg);
            System.out.println("localVar : " + localVar + "\n");
        };
        fi.method();
    }
}

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

한 개의 추상 메소드를 가지는 인터페이스들은 모두 람다식 사용 가능

표준 API 로 제공되는 함수적 인터페이스

java.util.function
매개타입으로 사용되어 람다식을 매개값으로 대입할 수 있도록 해줌

Consumer

매개값은 있고 리턴값이 없는 추상 메소드 accept() 가지고 있어 단지 매개값을 소비하는 역할만 수행

인터페이스 명추상 메소드설명
Consumer<T>void accept(T t)객체 T 받아서 소비
BiConsumer<T,U>void accept(T t, U u)객체 T와 U를 받아 소비
DoubleConsumervoid accept(double value)double 값 받아 소비
IntConsumervoid accept(int value)int 값 받아 소비
LongConsumervoid accept(long value)long 값 받아 소비
ObjDoubleConsumer<T>void accept(T t, double value)객체 T와 double 값 받아 소비
ObjIntConsumer<T>void accept(T t, int value)객체 T와 int 값 받아 소비
ObjLongConsumer<T>void accept(T t, long value)객체 T와 long 값 받아 소비

Supplier

매개값은 없고 리턴값만 있는 추상 메소드 getXXX() 가지고 있으며 실행 후 호출한 곳으로 데이터를 반환하는 역할

인터페이스 명추상 메소드설명
Supplier<T>T get()객체 리턴
BooleanSupplierBoolean getAsBoolean()boolean 값 리턴
DoubleSupplierdouble getAsDouble()double 값 리턴
IntSupplierint getAsInt()int 값 리턴
LongSupplierlong getAsLong()long 값 리턴

Function

매개값과 리턴값 모두 있는 추상 메소드 applyXXX() 가지고 있어 매개값을 리턴값으로 매핑(타입 변환)하는 역할

public class FunctionExample{
	private static List<Student> list = Arrays.asList(
    	new Student("aa", 50, 60);
        new Student("bb", 60, 20);
    );
    
    public static void avg(ToIntFunction<Student> function){
    	int sum = 0;
        for(Student student : list){
        	sum += function.applyAsInt(student);
        }
    	double avg = (double) sum / list.size();
    }
    
    
    public static void main(String[] args){
    	double engAvg = avg(t -> t.getEngScore());
        System.out.println(engAvg);
        
        double mathAvg = avg(t -> t.getMathScore());
        System.out.println(mathAvg);
    }
}

Operator

매개값과 리턴값 모두 있는 추상 메소드 가지고 있고 주로 매개값 연산하고 그 결과 리턴할 경우에 사용하며 Function 함수적 인터페이스와 동일한 형식이지만 매개값을 이용해서 연산 수행한 후 동일한 타입으로 리턴값 제공

Predicate

매개값을 조사해서 true, false 리턴할 때 사용하며 testXXX() 메소드를 가짐

디폴트 메소드

함수적 인터페이스는 디폴트 메소드를 가지고 있어 두 개의 함수적 인터페이스를 순차적으로 연결해서 실행할 수 있게 하며, 첫번째 리턴값을 두번째 매개값으로 제공해 최종 결과값을 리턴하게 해줌

  • andThen()
    인터페이스 A 실행 후 B 실행
    대부분의 함수적 인터페이스에서 제공함
Interface AB = InterfaceA.andThen(InterfaceB);
result = InterfaceAB.method();
  • compose()
    인터페이스 B 실행 후 A 실행
    Function<T,R>, DoubleUnaryOperator, IntUnaryOperator, LongUnaryOperator 에서만 제공함
InterfaceAB = InterfaceA.compose(InterfaceB);
result = InterfaceAB.method();

Consumer 종류의 함수적 인터페이스는 처리결과 반환값 없기때문에 andThen(), compose() 디폴트 메소드는 함수적 인터페이스의 호출 순서만 결정
Function/Operator 종류의 함수적 인터페이스는 먼저 실행한 함수적 인터페이스의 결과를 다음 함수적 인터페이스의 매개값으로 넘겨주고 최종 처리 결과를 반환

  • and(), or(), negate()
    Predicate 함수적 인터페이스의 디폴트 메소드로 and, or, not의 결과를 반환

정적 메소드

  • isEqual()
    Predicate<T>의 정적 메소드로 Objects.equals(sourceObject, targetObject) 실행

  • minBy(), maxBy()

BinaryOperator<T> 함수적 인터페이스의 정적 메소드로 Comparator를 이용해 최대 T, 최소 T를 얻는 BinaryOperator<T>를 반환

6. 메소드 참조

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

기본 메소드를 단순하게 호출만 하는 람다식을 간략화

IntBinaryOperator operator = (left, right) -> Math.max(left, right);
IntBinaryOperator operator = Math::max;

정적 메소드 참조

클래스 :: 메소드

IntBinaryOperator operator = (x,y) -> Calculator.staticMethod(x, y);

operator = Calculator::staticMethod;

인스턴스 메소드 참조

참조변수 :: 메소드

Calculator obj = new Calculator();
operator = (x,y) -> obj.instanceMethod(x, y);

operator = obj::instanceMethod;

메소드의 매개변수 참조

클래스::instanceMethod

ToIntBiFunction<String, String> function;

function = (a,b) -> a.compareToIgnoreCase(b);
print(function.applyAsInt("Java8", "JAVA8"));

function = String::compareToIgnoreCase;
print(function.applyAsInt("Java8", "JAVA8"));

생성자 참조

클래스::new

public Member(String id){
	this.id = id;
}
Function<String, Member> function1 = Member::new;
Member member1 = function1.apply("aaa");


public Member(String name, String id){
	this.name = name;
    this.id = id;
}
BiFunction<String, String, Member> function2 = Member::new;
Member member2 = function2.apply("aa", "ag");
profile
🐥

0개의 댓글

관련 채용 정보