김찬진의 개발 블로그
[23/07/14] 함수형 인터페이스, 람다식, 익명객체 본문
자바의 메서드는 클래스(또는 그의 인스턴스)에 종속되어 사용되어야 한다
람다식은 어떠한 클래스(또는 그의 인스턴스)에도 종속된 것 같지 않고 참조변수에 메서드를 대입하는 것처럼 보이지만 사실은 그렇지 않다
람다식은 함수형 인터페이스의 구현체에 종속되어 사용된다.
물론 new 연산자를 사용한 함수형 인터페이스의 구현체 생성 부분, 메서드 오버라이딩 부분이 생략되어 람다식에선 이 부분이 보이지 않을 수 있다.
람다식은 함수형 인터페이스를 구현하고 함수형 인터페이스에 구현된 유일한 메서드를 오버라이딩한 결과물인 익명 객체를 다루는 것이었다!
// 아래에 전체 코드있음
// java.util.function에 구현된 함수형 인터페이스를 사용한 람다식
Supplier<Integer> s = () -> (int)(Math.random()*100) + 1;
// 함수형 인터페이스의 구현체 생성 부분, 메서드 오버라이딩 부분을 생략하지 않은 오리지널 형태
Supplier<Integer> s2 = new Supplier<Integer>() {
@Override
public Integer get() {
return (int)(Math.random()*100 + 1);
}
};
// 직접 구현한 함수형 인터페이스를 사용한 람다식
MySupplier<Integer> s3 = new MySupplier<Integer>() {
@Override
public Integer get() {
return (int)(Math.random()*100 + 1);
}
};
함수형 인터페이스는 자신이 직접 구현해서 람다식을 다뤄도 되지만
java.util.function에 구현된 함수형 인터페이스를 사용하여 익명객체를 생성하고 메서드를 구현하는 것이 직관적이다.
아래 학습 코드에서 다음 3가지 사항을 유의하여 복습하자.
1. java.util.function 패키지에 구현되어 있는 Supplier 함수형 인터페이스를 활용한 람다식 (함수형 인터페이스 구현체 생성 부분과 오버라이딩 부분 생략)
2. java.util.function 패키지에 구현되어 있는 Supplier 함수형 인터페이스를 활용한 람다식 (함수형 인터페이스 구현체 생성 부분과 오버라이딩 부분 생략하지 않음)
2. 내가 직접 만든 함수형 인터페이스를 활용한 람다식
package functionalInterface;
import java.util.ArrayList;
import java.util.List;
import java.util.function.*;
@FunctionalInterface
interface MySupplier<T> {
public abstract T get();
}
@FunctionalInterface
interface MyConsumer<T> {
public abstract void accept(T t);
}
@FunctionalInterface
interface MyFunction<T, R> {
public abstract R apply(T t);
}
@FunctionalInterface
interface MyPredicate<T> {
public abstract boolean test(T t);
}
public class LambdaEx5{
public static void main(String[] args) {
// 자바의 메서드는 클래스(또는 그의 인스턴스)에 종속되어 사용되어야 한다
// 람다식은 어떠한 클래스(또는 그의 인스턴스)에도 종속된 것 같지 않지만 사실은 그렇지 않다
// 람다식은 함수형 인터페이스의 구현체에 종속되어 사용된다.
// 물론 new 연산자를 사용한 함수형 인터페이스의 구현체 생성 부분, 메서드 오버라이딩 부분이 생략되어 람다식에선 이 부분이 보이지 않을 수 있다.
// 함수형 인터페이스는 자신이 직접 구현해서 람다식을 다뤄도 되지만
// java.util.function에 구현된 함수형 인터페이스를 사용하여 익명객체를 생성하고 메서드를 구현하는 것이 직관적이다.
// 함수형 인터페이스 옆에 적힌 타입 변수는 지네릭 메서드임을 인지할 것. 메서드 내의 지역변수(반환타입, 매개변수의 타입)로 사용됨
// Runnable 의 void run() : 매개변수 없음, 반환값 없음
// Supplier<T> 의 T get() : 매개변수 없음, 반환값 1개
// Consumer<T> 의 void accept(T t) : 매개변수 1개, 반환값 없음
// Function<T, R> 의 R apply(T t) : 매개변수 1개, 반환값 1개
// Predicate<T> 의 boolean test(T t) : 매개변수 1개, 반환값 boolean
System.out.println("------------------- Supplier -------------------");
// java.util.function에 구현된 함수형 인터페이스를 사용한 람다식
Supplier<Integer> s = () -> (int)(Math.random()*100) + 1;
// 함수형 인터페이스의 구현체 생성 부분, 메서드 오버라이딩 부분을 생략하지 않은 오리지널 형태
Supplier<Integer> s2 = new Supplier<Integer>() {
@Override
public Integer get() {
return (int)(Math.random()*100 + 1);
}
};
// 직접 구현한 함수형 인터페이스를 사용한 람다식
MySupplier<Integer> s3 = new MySupplier<Integer>() {
@Override
public Integer get() {
return (int)(Math.random()*100 + 1);
}
};
System.out.println("------------------- Consumer -------------------");
// java.util.function에 구현된 함수형 인터페이스를 사용한 람다식
Consumer<Integer> c = i -> System.out.print(i + ", ");
// 함수형 인터페이스의 구현체 생성 부분, 메서드 오버라이딩 부분을 생략하지 않은 오리지널 형태
Consumer<Integer> c2 = new Consumer<Integer>() {
@Override
public void accept(Integer i) {
System.out.print(i + ", ");
}
};
// 직접 구현한 함수형 인터페이스를 사용한 람다식
MyConsumer<Integer> c3 = new MyConsumer<Integer>() {
@Override
public void accept(Integer i) {
System.out.print(i + ", ");
}
};
System.out.println("------------------- Function -------------------");
// java.util.function에 구현된 함수형 인터페이스를 사용한 람다식
Function<Integer, Integer> f = i -> i/10 * 10;
// 함수형 인터페이스의 구현체 생성 부분, 메서드 오버라이딩 부분을 생략하지 않은 오리지널 형태
Function<Integer, Integer> f2 = new Function<Integer, Integer>() {
@Override
public Integer apply(Integer i) {
return i/10 * 10;
}
};
// 직접 구현한 함수형 인터페이스를 사용한 람다식
MyFunction<Integer, Integer> f3 = new MyFunction<Integer, Integer>() {
@Override
public Integer apply(Integer i) {
return i/10 * 10;
}
};
System.out.println("------------------- Predicate -------------------");
// java.util.function에 구현된 함수형 인터페이스를 사용한 람다식
Predicate<Integer> p = i -> i%2==0;
// 함수형 인터페이스의 구현체 생성 부분, 메서드 오버라이딩 부분을 생략하지 않은 오리지널 형태
Predicate<Integer> p2 = new Predicate<Integer>() {
@Override
public boolean test(Integer i) {
return i%2==0;
}
};
MyPredicate<Integer> p3 = new MyPredicate<Integer>() {
@Override
public boolean test(Integer i) {
return i%2==0;
}
};
List<Integer> list = new ArrayList<>();
List<Integer> list3 = new ArrayList<>();
System.out.println("list: " + list);
System.out.println("list3: " + list3);
makeRandomList(s, list);
makeRandomList3(s3, list3);
System.out.println("랜덤 요소 리스트 만들기(original 방식 - Supplier)");
System.out.println(list);
System.out.println("랜덤 요소 리스트 만들기(custom 방식 - MySupplier)");
System.out.println(list3);
System.out.println();
System.out.println("각 3가지 방식에 대하여 짝수만 출력(original 방식 - Predicate, Consumer)");
printEvenNum(p, c, list);
System.out.println("각 3가지 방식에 대하여 짝수만 출력(custom 방식 - MyPredicate, MyConsumer)");
printEvenNum3(p3, c3, list);
System.out.println();
List<Integer> newList = doSomething(f, list);
List<Integer> newList3 = doSomething3(f3, list);
System.out.println("1의 자리 지우기(original 방식 - Function)");
System.out.println(newList);
System.out.println("1의 자리 지우기(custom 방식 - MyFunction)");
System.out.println(newList3);
}
static <T> List<T> doSomething(Function<T, T> f, List<T> list) {
List<T> newList = new ArrayList<T>(list.size());
for(T i : list) {
newList.add(f.apply(i));
}
return newList;
}
static <T> List<T> doSomething3(MyFunction<T, T> f, List<T> list) {
List<T> newList = new ArrayList<T>(list.size());
for(T i : list) {
newList.add(f.apply(i));
}
return newList;
}
static <T> void printEvenNum(Predicate<T> p, Consumer<T> c, List<T> list) {
System.out.print("[");
for(T i : list) {
if(p.test(i)) c.accept(i);
}
System.out.println("]");
}
static <T> void printEvenNum3(MyPredicate<T> p, MyConsumer<T> c, List<T> list) {
System.out.print("[");
for(T i : list) {
if(p.test(i)) c.accept(i);
}
System.out.println("]");
}
static <T> void makeRandomList(Supplier<T> s, List<T> list) {
for(int i=0; i<10; i++) {
list.add(s.get());
}
}
static <T> void makeRandomList3(MySupplier<T> s, List<T> list) {
for(int i=0; i<10; i++) {
list.add(s.get());
}
}
}
'1일1배움 > Java' 카테고리의 다른 글
[23/07/16] 공변성, 반공변성 (0) | 2023.07.16 |
---|---|
[23/07/16] 추상메서드와 인터페이스의 차이 (0) | 2023.07.16 |
[23/07/14] 지네릭 클래스, 제한된 지네릭 클래스, 와일드 카드, 지네릭 메서드 (0) | 2023.07.15 |
[23/04/19] Map, Set이 generics를 쓸 때 equals()와 hashcode()를 오버라이딩해라 (0) | 2023.04.19 |
[23/04/19] 자바빈 프로퍼티 규약이란? (0) | 2023.04.19 |
Comments