
// 위를 아래와 같이 생략 가능
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 );
}
};
람다식은 타입을 생략해도 컴파일러가 타입 추론을 통해 동작하게 해준다.
함수형 인터페이스란 딱 하나의 추상 메서드가 선언된 인터페이스 를 말한다.
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 도 생략이 가능하다.
함수형 인터페이스 : 추상 메서드가 1개만 정의된 인터페이스
함수형 인터페이스 중 자주 사용할 것 같은 것들을 표준 API 로 미리 만들어 제공해준다.
사용자는 굳이 추상 메서드를 가진 인터페이스를 일일히 정의할 필요 없이 API 만 사용하면 된다
API 는 import java.util.fonction 패키지로 제공된다.
Consumer 은 매개변수를 '소비'
Supplier 은 반환값을 '공급'
Function 은 T 를 R 로 매핑
// 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 도 지정된 제너릭 타입을 반환하는 함수형 인터페이스다.
<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( ) 을 사용하고 타입만 맞게 넣어주면 된다.
<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<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() 메서드를 사용한다.
<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( ) 로 동일하고 타입만 맞게 넣어주면 된다. )