함수는 간단한 식으로 표현하는 방법, 익명함수를 생성하기 위한 식이고, 자바에서는 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);
자주 사용되는 다양한 함수형 인터페이스를 제공하고, 같은 매개변수 타입과 리턴타입의 람다식을 참조 가능하다.

매개변수와 리턴 값 모두 없는 경우
package java.lang;
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
Runnable r = () -> System.out.println("출력문 테스트");
r.run(); // "출력문 테스트"
매개변수는 없고, 리턴값(타입)이 있는 경우
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);
매개 변수는 있지만, 리턴 타입은 없는 경우
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 사용해서 입력값 출력하기");
하나의 매개변수를 받아서 하나의 결과를 리턴
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);
조건식을 표현하는 데 사용, 매개 변수는 하나, 리턴타입은 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));