목차
- 함수형 인터페이스란?
- 람다 표현식?
- 람다 표현식 - 메소드 매개변수 활용
- 람다 표현식 - 변수로 저장
- 람다 표현식 - 리턴 타입 활용
- 자바에서 함수형 프로그래밍 / 일급 객체
함수형 인터페이스란?
public interface RunSomething {
void doIt(); // abstract 생략 가능
}
인터페이스에 추상 메서드가 1개만 존재하면 해당 인터페이스는 함수형 인터페이스이다.
함수형 인터페이스 = SAM(Single Abstract Method) 인터페이스
@FunctionalInterface
public interface RunSomething {
void doIt(); // abstract 생략 가능
public static void printName(){ // public 생략 가능
System.out.println("jeu");
}
public default void printAge(){ // public 생략 가능
System.out.println("27");
}
}
static 메서드 / default 메서드가 붙어도 추상 메서드가 1개만 존재하면 함수형 인터페이스이다.
static / default 메서드의 접근 제어자는 public만 가능한 이유
ex) private 또는 protected 사용 불가능
인터페이스의 static 메서드는 인터페이스에 고정된 기능을 제공하기 때문에 항상 public으로 접근 가능해야 한다.
자바 9
자바9 부터는 인터페이스에서는 default 메서드는 private 또한 사용 가능하다.
@FunctionalInterface 어노테이션 사용 이유
명시성 →
해당 인터페이스가 함수형 인터페이스임을 명확히 표시할 수 있다.
해당 인터페이스가 람다 표현식이나 메서드 참조로 사용될 것이라는 것을 다른 개발자에게 알린다.
컴파일러 체크 →
컴파일러는 @FunctionalInterface가 붙은 인터페이스가 실제로 함수형 인터페이스인지 확인
즉, 하나의 추상 메서드만 존재하는지를 체크하여, 두 개 이상의 추상 메서드가 존재하면 컴파일 오류를 발생
이는 실수로 여러 추상 메서드를 추가하는 것을 방지한다.
람다 표현식 (Lambda Expressions)?
함수형 인터페이스의 인스턴스를 만드는 방법으로 쓰일수 있다.
익명 클래스를 통한 인스턴스 생성 방식 보다 코드가 더 간결하다
또한 메소드 매개변수, 리턴 타입, 변수로 만들어 사용할 수도 있다.
자바8 이전 익명 클래스 사용 방식
public class Main {
public static void main(String[] args) {
// 자바8 이전 방식: 익명 내부 클래스 anonymous inner class
RunSomething runSomething = new RunSomething() {
@Override
public void doIt() {
System.out.println("Hello");
}
};
}
}
람다 표현식으로 변환
public class Main {
public static void main(String[] args) {
// 자바8 이전 방식: 익명 내부 클래스 anonymous inner class
RunSomething runSomething = () -> System.out.println("Hello");
runSomething.doIt();
}
}
람다 표현식 - 메소드 매개변수 활용
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name)); // 각 이름 출력
List forEach 메소드 매개변수에 람다 표현식(Consumer 인터페이스)을 넘겨주고 있다.
Iterable 인터페이스
public interface Iterable<T> {
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
Consumer (함수형 인터페이스)
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
람다 표현식 - 변수로 저장
// 람다 표현식 변수로 저장
Function<Integer, Long> square = x -> (long) x * x;
long result1 = square.apply(5); // 25
long result2 = square.apply(6); // 36 재활용
Function 인터페이스
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
람다 표현식 - 리턴 타입 활용
public class Main {
@FunctionalInterface
static interface Adder{
abstract int add(int a, int b);
}
public Adder getAdder(){
return (a, b) -> a + b;
}
public static void main(String[] args) {
// 람다 표현식 리턴 타입
Main main = new Main();
Adder adder = main.getAdder(); // Adder 인터페이스 반환
System.out.println(adder.add(5, 3));
}
}
자바에서 함수형 프로그래밍 / 일급 객체(First-Class-Object)
자바에서 함수형 프로그래밍
- 함수를 First class object로 사용할 수 있다.
- 고차 함수 (Higher-Order Function)
- 불변성
- 순수 함수 (Pure function)
일급 객체 / 고차 함수
1. 변수에 저장할 수 있다.
Function<Integer, Integer> square = x -> x * x; // 함수를 변수에 저장
2. 인자로 전달할 수 있다. 함수를 다른 함수의 인자로 전달할 수 있다.
고차함수 (Higher-Order-Function)
public void process(Function<Integer, Integer> func) {
int result = func.apply(5); // 전달된 함수를 사용
}
3. 함수에서 반환할 수 있다: 함수를 다른 함수의 반환값으로 사용할 수 있다.
고차함수 (Higher-Order-Function)
public Function<Integer, Integer> getMultiplier(int factor) {
return x -> x * factor; // 람다 표현식을 반환
}
4. 이름이 있을 수도 있고, 없을 수도 있다: 익명 함수(람다 표현식 등)를 사용할 수도 있다.
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.forEach(n -> System.out.println(n)); // 익명 함수 사용
함수를 일급 객체로 취급하는 예시
import java.util.function.Function;
public class Main {
// 함수가 매개변수로 전달됨
public static void process(Function<Integer, Integer> func) {
int result = func.apply(5); // func을 사용하여 5를 변환
System.out.println(result); // 결과 출력
}
// 함수가 반환됨
public static Function<Integer, Integer> getMultiplier(int factor) {
return x -> x * factor; // x에 factor를 곱하는 함수 반환
}
public static void main(String[] args) {
// 함수 변수를 사용
Function<Integer, Integer> square = x -> x * x;
process(square); // 25 출력
Function<Integer, Integer> doubleValue = getMultiplier(2);
System.out.println(doubleValue.apply(5)); // 10 출력
}
}
일급 객체 정리
일급 객체로서의 함수는 변수에 저장되고, 인자로 전달되며, 다른 함수에서 반환될 수 있다.
이러한 특성 덕분에 함수형 프로그래밍을 쉽게 구현할 수 있으며, 코드의 재사용성과 모듈성을 높일 수 있다.
따라서 "함수를 First-Class Object로 사용할 수 있다"는 것은 함수가 다른 데이터 타입처럼 자유롭게 다루어질 수 있음을 의미
순수 함수 (Pure function)
@FunctionalInterface
public interface RunSomething {
int doIt(int a, int b); // abstract 생략 가능
}
public static void main(String[] args) {
RunSomething runSomething = (a, b) -> {
return a + b;
};
int answer = runSomething.doIt(1, 3);
int answer1 = runSomething.doIt(1, 3);
int answer2 = runSomething.doIt(1, 3);
// 함수형 프로그래밍 answer1 == answer2 == answer3
}
함수형 프로그래밍에서 같은 값을 매개변수로 던져주면 항상 같은 값을 리턴해야 한다.
만약 같은 입력에 다른 입력이 나올 가능성이 있다면 해당 함수는 함수형 프로그래밍이라고 보기 어렵다.
- 입력에 대한 결정론적 결과
- 같은 입력 값이 주어지면 항상 같은 결과를 반환
- 예를 들어, doIt(1, 3)이라는 호출이 있을 때, 이 호출이 항상 4를 반환
- 부작용 없음
- 함수가 외부 상태를 변경하지 않으며, 함수 외부의 값을 읽지 않거나 수정하지 않는다.
- 함수 내부에서 수행되는 연산은 전적으로 입력 값에만 의존한다.
- 상태가 없음
- 함수의 실행이 외부 상태에 영향을 주지 않으며, 함수가 실행될 때마다 상태가 변하지 않는다.
- 상태 변화가 없는 구조로, 다른 코드와의 상호작용이 제한적
참고 자료
일급 객체란 무엇인지 한번에 파악이 가능하다.
'Computer Sience > Java' 카테고리의 다른 글
[JAVA8] 메소드 래퍼런스 (0) | 2024.09.30 |
---|---|
[JAVA8] 람다 표현식 + 변수 캡쳐 & 변수 쉐도잉 (0) | 2024.09.30 |
[JAVA8] 자바에서 제공하는 함수형 인터페이스 (0) | 2024.09.30 |
& VS && (0) | 2024.06.04 |
JVM Run Time Data Area (0) | 2024.05.17 |