메소드 참조
메소드 참조는 람다식으로 줄어든 코드의 양을 조금 더 줄일 수 있게 한다.
따라서 메소드 참조가 유용한 상황에서는 람다식이 아닌 메소드 참조를 사용해 해결 할 수 있다.
메소드 참조의 유형은 다음과 같이 4가지로 정리할 수 있다.
- static 메소드 참조
- 참조변수를 통한 인스턴스 메소드 참조
- 클래스 이름을 통한 인스턴스 메소드 참조
- 생성자 참조
static 메소드 참조
- 자바 8부터 이미 정의되어 있는 메소드를 사용할 때 람다식을 작성할 필요 없이 메소드 정보만 전달할 수 있다.
- 대입연산자 오른편에 메소드에 대한 정보만 전달되면 전달된 인자를 그대로 넘겨주면서 해당 메소드를 호출하라는 의미이다.
클래스 이름 :: static 메소드 이름
Consumer<List<Integer>> c = l -> Collections.reverse(l);
Consumer<List<Integer>> c = Collections::reverse;
// accept 메소드 호출시 전달되는 인자를
// reverse 메소드를 호출하면서 그대로 전달한다는 약속에 근거하여 수정
더보기
class MethodReferences_static {
public static void main(String[] args) {
List<Integer> li = Arrays.asList(1, 3, 5, 7, 9);
li = new ArrayList<>(li);
// Consumer<List<Integer>> c = l -> Collections.reverse(l); // 람다식
Consumer<List<Integer>> c = Collections::reverse; // 메소드 참조
c.accept(li); // 순서 뒤집기, 전달 인자 li를 reverse에 그대로 전달하게 됨
System.out.println(li); // 출력
}
}
// Consumer<T> void accept(T t)
[9, 7, 5, 3, 1]
참조변수를 통한 인스턴스 메소드 참조
- 참조변수가 final로 선언되었거나 effectively final인 경우에만 람다식과 메소드 참조를 허용한다.
- 한번 참조한 대상을 바꾸지 않는 참조변수를 effectively final(사실상 상수)이라 한다.
- 람다식이나 메소드 참조로 생성된 인스턴스 내에서 final로 선언되지 않았거나 effectively final이 아닌 참조변수를 참조하게 하는 것은 논리적 혼란 또는 예측 불가능한 상황으로 이어질 수 있기 때문에 제한을 두는 것이다.
참조변수 이름 :: 인스턴스 메소드 이름
Consumer<List<Integer>> c = e -> js.sort(e);
Consumer<List<Integer>> c = js::sort;
더보기
class MethodReferences_EffectivelyFinal {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1,3,5,7,9);
ls = new ArrayList<>(ls);
JustSort js = new JustSort();
// Consumer<List<Integer>> c = e -> js.sort(e); // 람다식
Consumer<List<Integer>> c = js::sort; // 메소드 참조
c.accept(ls);
System.out.println(ls);
}
}
// Consumer<T> void accept(T t)
[9, 7, 5, 3, 1]
🔍 forEach 메소드
- Iterable<T> 인터페이스에 정의 되어 있는 디폴트 메소드이다.
- Collection<E> 인터페이스는 Iterable<T> 인터페이스를 상속하기 때문에 대부분의 컬렉션 클래스들은 Iterable<T>를 구현하므로 forEach 메소드 호출이 가능하다.
- forEach 메소드 호출을 위해 Consumer<T> 인터페이스에 대한 람다식 또는 메소드 참조를 전달해야 한다.
default void forEach(Consumer<? super T> action) {
for (T t : this) // this는 이 메소드가 속한 컬렉션 인스턴스를 의미
action.accept(t) // 모든 저장된 데이터들에 대해 이 문장 반복
}
class MethodReferences_ForEach {
public static void main(String[] args) {
List<String> ls = Arrays.asList("Red", "Black");
// ls.forEach(s -> System.out.println(s)); // 람다식 기반
ls.forEach(System.out::println); // 메소드 참조 기반
}
}
Red
Black
Red
Black
인스턴스 없이 인스턴스 메소드 참조
- 첫번째 전달인자를 대상으로 인스턴스의 메소드를 호출하면 두번째 이후의 전달인자를 그대로 전달하기로 약속되어 있다.
클래스 이름 :: 인스턴스 메소드
class IBox {
private int n;
public IBox(int n) {
this.n = n;
}
public int larger(IBox b) { // 인스턴스 메소드
if (n > b.n)
return n;
else
return b.n;
}
}
class MethodReferences_NoObject {
public static void main(String[] args) {
IBox ib1 = new IBox(5);
IBox ib2 = new IBox(7);
// 두 상자에 저장된 값 비교하여 더 큰 값 반환
// ToIntBiFunction<IBox, IBox> bf = (b1, b2) -> b1.larger(b2); // 람다식 기반
ToIntBiFunction<IBox, IBox> bf = IBox::larger; // 메소드 참조 기반
int bigNum = bf.applyAsInt(ib1, ib2);
System.out.println(bigNum);
}
}
// ToIntBiFunction<T, U> int applyAsInt(T t, U u)
7
위 예제에서 applyAsInt 메소드가 호출되면 다음과 같이 실행된다.
- ib1.larger(ib2);
생성자 참조
- 람다식 작성 시 인스턴스 생성 후 이의 참조 값을 반환하는 경우 생성자 참조를 사용할 수 있다.
클래스이름 :: new
Function<char[], String> f = ar -> { return new String(ar); };
Function<char[], String> f = ar -> new String(ar);
Function<char[], String> f = String::new;
더보기
class MethodReferences_StringMaker {
public static void main(String[] args) {
// Function<char[], String> f = ar -> new String(ar); // 람다식
Function<char[], String> f = String::new; // 생성자 참조
char[] src = {'R', 'o', 'b', 'o', 't'};
String str = f.apply(src);
System.out.println(str);
}
}
// Function<T, R> R apply(T t)
Robot
'☕ Java > 이론' 카테고리의 다른 글
[Junit5] @ParameterizedTest 사용하기 (0) | 2022.10.11 |
---|---|
[Java] 추상 클래스 (0) | 2022.08.30 |
[Java] 스트림 (Stream) (0) | 2022.02.08 |
[Java] Optional 클래스 (0) | 2022.02.07 |
[Java] 함수형 인터페이스 (0) | 2022.02.03 |
[Java] 람다 (0) | 2022.01.28 |
[Java] 네스티드 클래스, 이너 클래스 (0) | 2022.01.27 |
[Java] 가변인자, 어노테이션 (0) | 2022.01.26 |