메소드 참조의 종류와, 사용법에 대해 알아보자.
메소드 참조의 종류
메소드 참조는 기본적으로 람다식보다 조금 더 코드를 간결하게 할 수 있다는 장점이 있다.
일부 람다식을 메소드 참조로 대신하게 할 수 있다.
// Collections 클래스의 reverse 메소드 기반 예제
public static void reverse(List<?> list) // 저장 순서를 뒤집는다.
class ArrangeList {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList(ls);
Consumer<List<Integer>> c = l -> Collections.reverse(l); // 저장 결과를 뒤집는 메소드
c.accept(ls); // 순서 뒤집기 진행
System.out.println(ls); // 출력
}
}
복습 차원에서 한번 더
Consumer<List<Integer>> c = l -> {
// Consumer<T> 인터페이스를 구현하기 위한 람다식 내용 or 몸체 자리.
}
Consumer<List<Integer>> c = l -> Collections.reverse(l); // 람다식이 아닌 웬 메소드....??
Consumer<List<Integer>> c = l -> Collections.reverse(l); // 인자가 무엇인지 잘 체크 해보자.
c.accept(ls);
1. accept Method에 List ls 값이 전달 된다.
2. accept Method에서 ls 값을 통해 로직을 구현.
3. Collections.reverse("인자"); 가 실행이 된다.
핵심 : 이때 위에서 전달한 l이 reverse()의 인자로 전달이 된다.
before : Consumer<List<Integer>> c = l -> Collections.reverse(l);
after : Consumer<List<Integer>> c = Collections::reverse; // 변경된 코드
static 메소드의 참조
class JustSort {
public void sort(List<?> lst) { // 인스턴스 메소드
Collections.reverse(lst);
}
}
class ArrangeList3 {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
JustSort js = new JustSort(); // js는 effectively final
Consumer<List<Integer>> c = e -> js.sort(e); // 람다식 기반
c.accept(ls);
System.out.println(ls);
}
}
Consumer<List<Integer>> c = js::sort; // 왼쪽 같이 변경이 될 수 있다.
class JustSort {
public void sort(List<?> lst) { // 인스턴스 메소드
Collections.reverse(lst);
}
}
class ArrangeList3 {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
JustSort js = new JustSort(); // js는 effectively final
Consumer<List<Integer>> c = e -> js.sort(e); // 람다식 기반
c.accept(ls);
System.out.println(ls);
}
}
JustSort js = new JustSort(); // js는 effectively final
effectively final
class ArrangeList3 {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
JustSort js = new JustSort(); // js는 effectively final
Consumer<List<Integer>> c = e -> js.sort(e); // 람다식 기반
c.accept(ls);
System.out.println(ls);
js = null; // js에 null을 삽입
}
}
위 같이 프로그램이 종료되는 시점에 null을 넣는 경우 effectively final이라 할 수 있을까?
강의에서는 컴파일이 되지 않는다 했지만, 필자의 IDE에서는 컴파일이 가능했다..
class ForEachDemo {
public static void main(String[] args) {
List<String> ls = Arrays.asList("Box", "Robot");
ls.forEach(s -> System.out.println(s)); // 람다식 기반
ls.forEach(System.out::println); // 메소드 참조 기반
}
}
default void forEach(Consumer<? super T> action) {
for (T t : this) // this는 이 메소드가 속한 컬렉션 인스턴스를 의미함
action.accept(t); // 모든 저장된 데이터들에 대한 이 문장 반복
}
List<String> lst = Arrays.asList("Box", "Robot", "Pen");
lst.forEach(data -> System.out.println(data));
// lst 컬렉션에 존재하는 data를 하나씩 꺼내 출력하는 예제.
이번에 나오는 메소드 참조는 위에서 설명한 약속과 다른 부분이 있다 말하고 있다.
class IBox {
private int n;
public IBox(int i) { n = i; }
public int larger(IBox b) {
if(n > b.n)
return n;
else
return b.n;
}
}
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)
// 약속에 근거한 줄인 표현
ToIntBiFunction<IBox, IBox> bf = IBox::larger;
체크해야 하는 부분
IBox.larger → 이런 형식으로 메소드를 호출 하려면 해당 메소드가 static Method로 선언이 되어야 한다.
하지만 현재 static Method가 아닌 인스턴스 메소드로 선언이 되있는 상태.
약속?? → 위와 같이 선언이 되었을때는 첫번째 인자로 들어오는 인스턴스의 메소드를 호출하고, 두번째 인자를
첫번째 메소드의 매개변수 인자로 전달하겠다는 약속이 내포되어 있다.
람다식을 메소드 참조로 변경, 메소드 참조를 람다식으로 변경
인스턴스 메소드 참조
class StringMaker {
public static void main(String[] args) {
Function<char[], String> f = ar -> {
// char[]을 받아 String으로 반환 한다.
return new String(ar);
};
char[] src = {'R', 'o', 'b', 'o', 't'};
String str = f.apply(src);
System.out.println(str);
}
}
// Function<T , R> R apply(T t);
// 예제 01
R apply(T t) {
// t -> char[]
return new R(t);
}
// 결론 01
before : Function<char[], String> f = ar -> new String(ar);
after : Function<char[], String> f = String::new; // 이런 형태로 사용이 가능