람다 표현식 사용하기 : 함수형 인터페이스
아무런 클래스의 메서드나 추상 클래스의 메서드를 람다 표현식으로 줄일 수 있는 것이 아니고, 함수형 인터페이스만 람다식으로 표현이 가능함
[ 함수형 인터페이스 ]
- 1개의 추상메서드를 가진 인터페이스
- 인터페이스에 정의된 하나의 추상 메서드는, 함수형 인터페이스의 구현체인 람다 표현식을 실행시키는 메서드
- 즉, 람다식으로 표현이 가능한 인터페이스를 가리켜서 함수형 인터페이스
함수형 인터페이스
개념
- 함수형 인터페이스는 딱 1개의 추상 메서드만 선언되어 있는 인터페이스
- 람다식은 함수형 인터페이스 안에 정의된 1개의 추상 메서드의 구현 부분을 짧게 표현한 것
- 즉, 람다식으로 구현하는 메서드는 인터페이스의 추상메서드여야 함
[ 함수형 인터페이스 ]
// 함수형 인터페이스 : 추상 메서드가 1개
interface InterA {
int add(int x, int y);
}
// 함수형 인터페이스 X : 추상 메서드가 2개
interface InterB {
int add(int x, int y);
int min(int x, int y);
}
함수형 인터페이스 선언
interface MyInterface{
public abstract int method(int a);
}
- 메서드의 반환타입, 매개변수의 개수, 매개변수 타입을 확인할 것!
- 위의 함수형 인터페이스를 보고 알 수 있는 것은
- 반환 값이 int이고, int형의 매개변수를 1개를 받는 메서드를 람다식으로 만든다는 뜻
@FunctionalInterface 사용하기
1. 개념
- @FunctionalInterface로 지정하면
- 인터페이스에 추상 메서드가 1개만 선언되었는지를 컴파일러가 체크할 수 있음
- 즉 해당 어노테이션을 인터페이스 선언부에 붙이면, 컴파일러가 해당 인터페이스를 함수형 인터페이스로 인식
- 이렇게 명시된 함수형 인터페이스에 2개 이상의 메서드가 선언되면 오류를 발생시킴
2. 에러 발생 확인
3. 형식
@FunctionalInterface
interface MyInterface { // 함수형 인터페이스
public abstract void methodA(); // 추상메서드가 2개 이상이면 컴파일러 발생
}
// 인터페이스명 변수 = 람다식
MyInterface mi = () → {
System.out.println("추상메서드를 람다식으로 구현")
}
// 메서드 호출
mi.methodA();
함수형 인터페이스 : 람다 표현식을 하나의 변수에 대입할 때 사용하는 참조변수의 타입
- 람다 표현식을 사용할 때는, 람다 표현식을 저장하기 위한 참조변수 타입을 결정해야 하는데
- 람다 표현식을 하나의 변수에 대입할 때 사용하는, 참조변수의 타입을 함수형 인터페이스라고 함
인터페이스명 참조변수 = 람다 표현식
함수형 인터페이스를 선언하고, 추상메서드를 람다식으로 표현하기
interface IFruit {
public String eat();
}
public class Main {
public static void main(String[] args) {
// 인터페이스명 변수 = 람다식
IFruit fruit = () -> {
return "과일을 먹습니다";
}
fruit.eat();
// 인터페이스명 변수 = new 인터페이스명() {}
IFruit fruit2 = new IFruit () {
@Override
public String eat() {
return "과일을 먹습니다";
}
}
fruit2.eat();
}
}
람다식으로 구현된 메서드를 보면 메서드를 변수로 선언하는 것처럼 보이지만, 실제로 자바에서는 메서드를 단독으로 선언할 수 없음
- 람다 표현식을 함수형 인터페이스타입 변수에 대입하고, 변수를 통해 메서드를 호출하여 사용하는 것
- 참조변수를 통해 메서드를 호출하여 사용하는 모습은, 마치 객체와 다름이 없음
- 즉, 람다식도 결국은 객체이고
- 인터페이스 익명 구현 객체를 짧게 표현하여 사용
- 그렇다고 익명 클래스와 람다 표현식이 같은 것은 아님
인터페이스 익명 구현 객체
1. 익명 클래스를 주로 사용하는 곳은, 인터페이스를 익명 클래스로 구현하여 사용할 때임
2. 원래는 클래스가 인터페이스를 구현하고, 인터페이스를 구현한 클래스의 객체를 생성한 후에, 메서드를 사용할 수 있는데
3. 익명 클래스를 이용하여 인터페이스를 구현하는 클래스를 별도로 생성하지 않고, 인터페이스를 바로 구현하여서 객체를 만들기 때문에 인터페이스 익명 구현 객체(익명으로 인터페이스를 구현한 객체)라고 함
4. 익명 클래스는 일회성으로 사용하는 메서드를 구현하기 위해서 주로 사용하는데, 추상화 구조인 인터페이스를 일회용으로 구현하여 사용할 필요가 있을 때 익명 구현 객체로 선언하여 이용하면 좋음
interface IFruit { public String eat(); // 추상메서드 public String wash(); }
public class Main { public static void main(String[] args) { // 인터페이스 익명 구현 객체 // 인터페이스타입 변수 = new 인터페이스 타입 IFruit fruit = new IFruit() { @Override public String eat() { return "과일을 먹습니다"; } @Override public String wash() { return "과일을 씻습니다"; } }; // 인터페이스 구현 객체 사용 fruit.eat(); fruit.wash(); } }
예제
@FunctionalInterface
interface MyFunctInterface {
public void methodA();
// public void methodB(); // 추상메서드 2개 이상이면 에러 발생
}
public class Test {
public static void main(String[] args) {
// 1. 익명 클래스를 이용하여 추상메서드 구현
MyFunctInterface mi1 = new MyFunctInterface() {
@Override
public void methodA() {
System.out.println("methodA() 호출");
}
};
mi1.methodA(); // methodA() 호출
// 1-1. 익명 클래스를 이용하여 추상메서드 구현
new MyFunctInterface() {
@Override
public void methodA() {
System.out.println("methodA() 호출");
}
}.methodA();
// 2. 람다식 표현: 매개변수와 리턴 타입이 없는 메서드
MyFunctInterface mi2 = () -> {
System.out.println("methodA() 호출");
};
mi2.methodA(); // methodA() 호출
// 2-1. 실행문이 1줄인 경우, 중괄호 {} 생략 가능
MyFunctInterface mi3 = () -> System.out.println("methodA() 호출");
mi3.methodA(); // methodA() 호출
}
}
함수형 인터페이스의 종류
- java.util.function에는 자바의 빌트인 함수형 인터페이스가 40여개가 있음
- 제공하는 함수형 인터페이스를 이용하여 프로그래밍할 수 있고
- 필요한 경우 구현하여 사용할 수 있음
인터페이스명 | 추상 메소드 | 설명 |
Runnable | void run() | 기본적인 인터페이스, 매개변수와 반환값 없음 |
Supplier<T> | T get() | 매개변수 없음, 제네릭타입 반환값 가짐 |
Consumer<T> | void accept() | 제네릭 매개변수 하나, 반환값 없음(void) |
Predicate<T> | boolean test() | 제네릭 매개변수 하나, Boolean 반환값 하나 |
Function<T,R> | R apply(T t) | 제네릭 매개변수 하나와 다른 제네릭 반환값하나 |
Comparator<T> | int compare(T o1, T o2) | 같은 제네릭 타입 매개변수 두개를 받고, Integer 반환값 하나 가짐, 객체간 비교를 위핸 compare를 위한 인터페이스 |
BiConsumer<T,U> | void accept(T t, U u) | 서로다른 제네릭 매개변수 두개를 받고 반환값 없음 |
BiFunction<T,U,R> | R apply(T t, U u) | 서로 다른 제네릭 매개변수 두개를 받고 다른 제네릭 타입의 반환값 하나 |
BiPridicate<T,U> | boolean test(T t, U u) | 서로 다른 제네릭 타입의 매개변수 2개를 받고 Boolean 타입의 반환값 하나 가짐 |
출처
'JAVA' 카테고리의 다른 글
[JAVA] 제네릭 타입 파라미터 범위를 한정(제한) (0) | 2024.05.03 |
---|---|
[JAVA] 람다식의 메서드 참조 (1) | 2024.05.02 |
[JAVA] Optional의 개념과 사용법, 주의할 점 (0) | 2024.05.01 |
[JAVA] 스트림이 동작할 때(생성, 중간 연산, 최종 연산) 사용하는 메서드 (0) | 2024.05.01 |
[JAVA] 스트림 API(특징, 스트림의 동작 흐름, 컬랙션과의 차이) (1) | 2024.05.01 |