TIL / JAVA 6주차(1) / 람다식

병아리코더 아카이브·2023년 9월 20일

JAVA

목록 보기
20/20
post-thumbnail

람다 표현식

  • 자바의 메서드를 간결한 함수 식으로 표현한 것이다.
// 위를 아래와 같이 생략 가능
int add(int a, int b) { return a+b; }
(a,b) -> a+b;
  • 람다식 표현에서는 인터페이스를 구현하는 클래스를 작성하고 객체를 생성하지 않아도 메서드를 사용할 수 있다.

  • 람다식은 이름없는 익명함수 (Anonymous Function ) 로 불리는 객체다.
    정확히 말하면 인터페이스를 익명 클래스로 구현한 익명 구현 객체 를 짧게 표현한 것이다.

  • 람다식은 아무 클래스나 추상 클래스의 메서드를 줄이지 못한다.
    오로지 인터페이스로 선언한 익명객체만이 람다식으로 표현이 가능하다.

// 인터페이스는 규격만 존재하기 때문에 객체화가 되지 않는다. 
// 인터페이스를 강제로 객체화하려고 할 때 익명 객체가 생성된다.
// 여기서 Runnable 은 인터페이스다.
Runnable task1 = new Runnable() {
	@Override
	public void run() {
		for (int i = 1; i < 3; i++) {
			System.out.println("익명객체 thread run - " + i );
		}
	}
};

// 람다식으로 아래와 같이 표현이 가능하다.
// new , 생성자 , 메서드 타입 , 메서드명 이 생략되었다.
Runnable task2 = () -> {
	for (int i = 1; i < 3; i++) {
		System.out.println("람다식 객체 thread run - " + i );
	}
};
  • Collection 의 요소를 필터링하거나 매핑할때 주로 쓰인다.

람다식은 타입을 생략해도 컴파일러가 타입 추론을 통해 동작하게 해준다.


함수형 인터페이스

  • 함수형 인터페이스란 딱 하나의 추상 메서드가 선언된 인터페이스 를 말한다.

  • JAVA 8 이후로 인터페이스에서도 일반메서드, 정적메서드 , final 상수 등이 허용이 되었는데 이것들은 추상 메서드가 아니기 때문에 존재해도 추상메서드만 1개면 함수형 인터페이스로 람다식이 가능하다.

// 람다식을 사용하려면 인터페이스가 1개의 추상메서드만 있어야 한다.
// 2개 추상 메서드를 가진 인터페이스는 람다식으로 사용 불가능.
// 이를 사전에 체크하기 위해 @FunctionalInterface 어노테이션 활용
NormalInter inter = new NormalInter() {
	@Override
	public void sayMsg(String msg) {
	}
	@Override
	public int operation(int a, int b) {
		return 0;
	}
};
  • 함수형 인터페이스를 만들 때 두 개 이상의 추상 메서드가 선언되지 않도록 인터페이스에 @FunctionalInterface 를 선언해주면 두 개 이상의 추상메서드 선언 시 오류를 발생시킨다.

람다식 사용

// 매개변수의 타입을 생략할 수 있다.
// 실행문이 return 이나 syso 밖에 없으면 {} 도 생략이 가능하다.
// ( 여기선 생략 안함 )
OperationA operA = (a, b) -> {
	System.out.println("합계 : " + (a + b)); 
};
		
// 만들때는 람다식으로 생략이 가능하지만 쓸 때는 plus 메서드가 나온다.
operA.plus(3, 4);
  • 매개변수의 타입을 생략할 수 있다.

  • return 이나 System.out.println 밖에 없으면 { } 중괄호 생략이 가능하다.

  • 람다식 이용시에는 생략한 메서드 명을 써야 한다.

// 매개변수가 하나일 경우 () 생략 가능
OperationB operB = a -> {
	System.out.println(a + " 의 제곱은 " + (a*a));
};

// 매개변수가 없을 경우에는 생략 할 수 있는게 없음
OperationC operC = () -> System.out.println("매개변수 없이 동작"); 

// 매개변수 1개에 return 만 존재하면...
// return 도 생략 가능
OperationD operD = msg -> msg; 
  • 매개변수가 하나면 ( ) 괄호 생략이 가능하다.

  • 매개변수가 없으면 ( ) 괄호 생략은 불가능

  • return 밖에 없으면 return 도 생략이 가능하다.


함수형 인터페이스 표준 API

  • 함수형 인터페이스 : 추상 메서드가 1개만 정의된 인터페이스

  • 함수형 인터페이스 중 자주 사용할 것 같은 것들을 표준 API 로 미리 만들어 제공해준다.

  • 사용자는 굳이 추상 메서드를 가진 인터페이스를 일일히 정의할 필요 없이 API 만 사용하면 된다

API 는 import java.util.fonction 패키지로 제공된다.

Consumer 은 매개변수를 '소비'
Supplier 은 반환값을 '공급'
Function 은 T 를 R 로 매핑

Runnable

// Runnable 은 스레드 생성 시 많이 사용된다.
// 람다식으로 생략 안했을 때
Thread thread = new Thread(new Runnable(){
	@Override
    public void run(){
    	System.out.println("Thread")
    }
});

// 람다식으로 생략
Thread thread = new Thread(()->System.out.println("Thread"));

// thread.start(); 를 하면 Runnable 의 run() 메서드의 내용이 실행된다.
  • run() 메서드를 사용한다.

  • 매개변수를 사용 안하고 반환값도 없는 함수형 인터페이스다.

Thread pool 에 쓰이는 Callable 도 지정된 제너릭 타입을 반환하는 함수형 인터페이스다.

Consumer<T>

// 람다식으로 생략 안했을 때
Consumer<String> con = new Consumer<String>() {
	@Override
	public void accept(String t) {
		System.out.println("입력값 : "+ t);
	}
};

// 람다식으로 생략
Consumer<String> con = t -> System.out.println("입력값 : "+ t);

// 메서드 실행
con.accept("Consumer");
  • T 타입 매개변수 t 를 사용하지만 반환값은 없는 함수형 인터페이스

  • accept(T t) 메서드를 사용한다.

  • BiConsumer<T,U> , XXXConsumer ,ObjXXXConsumer<T> 형태의 함수형 인터페이스도 존재한다.

XXX 에는 들어가는 매개변수 타입을 넣어주면 된다.
메서드는 accept( ) 을 사용하고 타입만 맞게 넣어주면 된다.

  • 인자값이 3개 이상은 직접 인터페이스 정의해야 한다.

Supplier<T>

// 람다식으로 생략 안했을 때
Supplier<MyClass> sup = new Supplier<MyClass>() {
	@Override
	public MyClass get() {
		return new MyClass();
	}
};

// 람다식으로 생략
Supplier<MyClass> sup = () -> new MyClass();

// 메서드 참조 (여기까지 생략할 수 있다.)
Supplier<MyClass> sup = MyClass::new;

// 실행
sup.get();
  • 매개변수를 사용하지 않지만 반환값은 있는 함수형 인터페이스

  • get() 메서드를 사용한다.

  • 이때 제네릭 타입 T 는 반환되는 타입을 의미한다.

  • XXXSupplier 형태도 존재한다

이때 XXXSupllier 은 타입에 따라 getAsXXX() 메서드를 사용한다.

Function<T, R>

// 람다식 생략 안했을 때
Function<String, Integer> func = new Function<String, Integer>() {
	@Override
	public Integer apply(String t) {
		return Integer.parseInt(t);
	}
};
        
// 람다식 생략
Function<String, Integer> func = t -> Integer.parseInt(t);

// 실행
func.apply("10");
  • T 타입 을 가진 매개변수 t 를 매핑(=타입변환) 해서 R 타입으로 반환한다.

  • apply(T t) 메서드를 사용한다.

매핑 : 여러 데이터 항목이 있는 객체에서 특정 타입의 값을 추출하거나 또는 다른 타입으로 변환하는 작업

  • BiFunction<T,U,R> , XXXFunction<R> , XXXtoYYYFunction , toYYYFunction<T> , toYYYBiFunction<T,U> 형태의 함수형 인터페이스도 존재한다.

이때 XXXtoYYYFunction, toYYYFunction , toYYYBiFunction 은 applyAsYYY() 메서드를 사용한다.

Predicate<T>

// Student 클래스 생성
class Student {
	String name;
	public Student(String name) {
		this.name = name;
	}
}
// Main 메서드 클래스
public class Main {
	public static void main(String[] args) {
    
		// Student 클래스 객체화
		Student stu = new Student("이민지");
        
		// 람다식 생략 안할 때
		Predicate<Student> prdc = new Predicate<Student>() {
			@Override
			public boolean test(Student t) {
				return t.name.contains("김") ? true : false;
			}
		};
        
        // 람다식 생략
        prdc = t -> t.name.contains("김") ? true : false;
        
        // 실행 출력
		System.out.println(prdc.test(stu));
	}
}
  • 매개 타입 T 의 변수 t 를 받아 true/false 를 반환한다.

  • test(T t) 메서드를 사용한다.

  • BiPredicate<T, U> , XXXPredicate 형태의 함수형 인터페이스도 존재한다.
    ( 메서드는 test( ) 로 동일하고 타입만 맞게 넣어주면 된다. )

0개의 댓글