✔ 메소드 참조
메소드 참조의 4가지 유형과 메소드 참조의 장점
- 람다식은 결국 메소드의 정의이므로 람다식을 작성할 때 이미 정의되어 있는 메소드를 사용한다면
람다식을 작성할 필요 없이 메소드 정보만 전달하여 이를 대신할 수 있게 됨 - 메소드 참조의 유형은 총 4가지
- static 메소드 참조
- 참조변수를 통한 인스턴스 메소드 참조
- 클래스 이름을 통한 인스턴스 메소드 참조
- 생성자 참조
- 그리하여 메소드 참조는 람다식으로 줄어든 코드의 양을 조금 더 줄일 수 있게 함
static 메소드 참조
- static 메소드의 참조 방법은 className::staticMethodName 사용
- 람다식은 이미 정의되어 있는 메소드를 사용
- 전달되는 인자를 람다식에 그대로 전달하므로 인자 전달 정보는 생략할 수 있음
// static 메소드
public static void reverse(List<?> list)
// 람다식 작성
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
Conumer<List<Integer>> c = l -> Collections.reverse(l);
c.accpet(ls);
// 메소드 참조 방식
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
Conumer<List<Integer>> c = Collections::reverse;
c.accpet(ls); // 전달 인자를 reverse에 그대로 전달
참조변수를 통한 인스턴스 메소드 참조
- 인스턴스가 존재할 때 인스턴스 메소드의 참조 방법은 ReferenceName::instanceMethodName 사용
- 람다식은 같은 지역에 선언된 참조변수에 접근하여 사용
- 이때 참조변수는 final로 선언되었거나 다른 인스턴스를 참조 하지 않는 effectively final이어야 함
class JustSort {
// 인스턴스 메소드
public void sort(List<?> lst) {
Collections.reverse(list);
}
}
// 람다식 작성
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
JustSort js = new JustSort();
Conumer<List<Integer>> c = e -> js.sort(e);
c.accpet(ls);
// 메소드 참조 방식
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
JustSort js = new JustSort();
Conumer<List<Integer>> c = js::sort;
c.accpet(ls); // 전달 인자를 reverse에 그대로 전달
// Iterable<T>를 대부분 구현하게 되는 컬렉션 클래스들
public interface Iterable<T> {
default void forEach(Consumer<? super T> action) {
for (T t : this)
action.accpet(t);
}
}
public interface Consumer<T> {
void accept(T t);
}
// 람다식 작성
List<String> ls = Arrays.asList("Box", "Robot");
ls.forEach(s -> System.out.println(s));
// 메소드 참조 방식
List<String> ls = Arrays.asList("Box", "Robot");
ls.forEach(System.out::println); // 전달 인자를 println에 그대로 전달
클래스 이름을 통한 인스턴스 메소드 참조
- 인스턴스가 존재하지 않을 때 인스턴스 메소드의 참조 방법은 ClassName::instanceMethodName 사용
- 람다식은 클래스 이름을 통해 첫 번째 전달인자를 대상으로 전달된 인스턴스의 메소드를 사용하도록 약속되어 있음
class IBox {
private int n;
public Box(int i) {
n = i;
}
public int larger(IBox b) {
if(n > b.n)
return n;
else
return b.n;
}
}
// 람다식 작성
IBox ib1 = new IBox(5);
IBox ib2 = new IBox(7);
ToIntBiFunction<IBox, IBox> bf = (b1, b2) -> b1.larger(b2);
int bigNum = bf.applyAsInt(ib1, ib2);
// 메소드 참조 방식
IBox ib1 = new IBox(5);
IBox ib2 = new IBox(7);
ToIntBiFunction<IBox, IBox> bf = IBox::larger;
int bigNum = bf.applyAsInt(ib1, ib2);
생성자 참조
- 생성자의 참조 방법은 ClassName::new 사용
- 단순히 인스턴스를 생성하고 이의 참조 값을 반환해야 하는 경우 사용
// 람다식 작성
Function<char[], String> f = ar -> new String(ar);
char[] src = {'R', 'o', 'b', 'o', 't');
String str = f.apply(src);
// 메소드 참조 방식
Function<char[], String> f = String::new;
char[] src = {'R', 'o', 'b', 'o', 't');
String str = f.apply(src);
✔ Optional 클래스
Optional 클래스의 기본
- Optional은 멤버 value에 인스턴스를 저장하는 일종의 래퍼 클래스
- Optional 인스턴스의 생성 방법으로는 of 메소드와 ofNullable 메소드가 존재
- of 메소드는 null을 인자로 전달할 수 없으므로 null을 전달할 경우 NullPointerException이 발생
- ofNullable 메소드는 null을 인자로 전달할 수 있으므로 비어 있는 Optional 인스턴스를 생성할 수 있음
- Optional 클래스의 isPresent 메소드를 통해 내용물의 존재여부를 확인하거나 get 메소드를 통해 꺼낼 수 있음
- Optional 클래스를 사용하여 ifPresent가 호출되었을 때 Optional 인스턴스가 저장하고 있는 내용물이 있으면,
이 내용물이 인자로 전달되면서 accept 메소드가 호출되도록 하여 if문이 사라지도록 줄일 수 있게 됨
- Optional 클래스를 사용하여 ifPresent가 호출되었을 때 Optional 인스턴스가 저장하고 있는 내용물이 있으면,
Optional<String> os1 = Optional.of(new String("Toy1"));
Optional<String> os2 = Optional.ofNullable(new String("Toy2"));
// Optional 클래스의 메소드
public void ifPresent(Consumer<? super T> consumer)
// if문
if(os1.isPresent())
System.out.println(os1.get());
// 람다식 작성
os1.ifPresent(s -> System.out.println(s));
// 메소드 참조 방식
os1.ifPresent(System.out::println);
Optional 클래스를 가지고 if ~ else문을 대신해보자
- Optional 클래스의 map 메소드는 apply 메소드가 반환하는 대상을 Optional 인스턴스에 감싸서 반환하게 됨
- 그러므로 반환 값을 대상으로 이어서 다음 메소드를 호출할 수 있음
public <U> Optional<U> map(Function<? super T, ? extends U> mapper)
Function<T, U> U apply(T t)
Optional<String> os1 = Optional.of("Optional String");
Optional<String> os2 = os1.map(s -> s.toUpperCase()); // Optional 인스턴스로 감싸서 반환
Optional<String> os3 - os1.map(s -> s.replace(' ', '_'))
.map(s -> s.toLowerCase());
- Optional 클래스의 orElse 메소드는 Optional 인스턴스에 저장된 내용물을 반환하며,
내용물이 없을 경우에 대신 반환할 대상을 지정할 수 있음- get 메소드와 유사하지만, orElse를 호출하면서 전달된 인스턴스를 반환할 수 있음
Optional<String> os1 = Optional.empty();
Optional<String> os2 = Optional.of("So Basic");
String s1 = os1.map(s -> s.toString())
.orElse("Empty");
- 그러므로 Optional 클래스의 map 메소드와 orElse 메소드를 이용하면 if ~ else문을 대신할 수 있음
class ContInfo {
String phone;
String adrs;
public ContInfo(String ph, String ad) {
phone = ph;
adrs = ad;
}
public String getPhone() {
return phone;
}
public String getAdrs() {
return adrs;
}
}
// if ~ else문
if(addr != null)
System.out.printl(addr);
else
System.out.println("There is no address.");
// Optional 클래스의 map과 orElse를 사용
Optional<ContInfo> ci = Optional.of(new ContInfo(null, "Republic of Korea"));
String addr = ci.map(c -> c.getAdrs())
.orElse("There is no address.");
System.out.println(addr);
Optional 클래스의 flatMap 메소드
- 위의 경우 메소드의 정의와 이를 호출하는 문장을 제외하고는 Optional 관련 코드를 볼 수 없음
- 그러나 경우에 따라서 Optional 클래스를 코드 전반에 사용하기 위해서는 map 메소드와 성격이 유사한 flatMap 메소드를 사용
- map과 flatMap 모두 Optional 인스턴스를 반환함
- 다만 map은 람다식이 반환하는 내용물을 Optional 인스턴스로 감싸는 일을 알아서 해주지만
flatMap은 알아서 해 주지 않기 때문에 람다식을 포함하고 있어야 함 - 하지만 Optional로 감싸서 반환하는 map 메소드는 get 메소드 호출을 통해서 꺼내는 과정을 거쳐야 되지만
flatMap 메소드는 그렇지 않으므로 Optional 인스턴스를 클래스의 멤버로 두어 코드 전반에 사용하는 경우에 유용
class ContInfo {
Optional<String> phone; // null일 수 있음
Optional<String> adrs; // null일 수 있음
public ContInfo(Optional<String> ph, Optional<String> ad) {
phone = ph;
adrs = ad;
}
public Optional<String> getPhone() {
return phone;
}
public Optional<String> getAdrs() {
return adrs;
}
}
Optional<ContInfo> ci = Optional.of(new ContInfo(Optional.ofNullable(null), Optional.of("Republic of Korea")));
String addr = ci.flatMap(c -> c.getAdrs()) // ci.map(c -> c.getAdrs()).get() 필요
.orElse("There is no address.");
System.out.println(addr);
✔ OptionalInt, OptionalLong, OptionalDouble 클래스
Optional과 OptionalXXX와의 차이점
- OptionalXXX 클래스들은 Optional 클래스보다 기능이 제한적이지만 스트림에서 사용하게 됨
// Optional
Optional<Integer> oi1 = Optional.of(3);
Optional<Integer> oi2 = Optional.empty();
oi1.ifPresent(i -> System.out.println(i + "\t"));
oi2.ifPresent(i -> System.out.println(i));
System.out.println(oi1.orElse(100) + "\t");
System.out.println(oi2.orElse(100) + "\t");
// OptionalXXX
OptionalInt oi1 = OptionalInt.of(3);
OptionalInt oi2 = OptionalInt.empty();
oi1.ifPresent(i -> System.out.println(i + "\t"));
oi2.ifPresent(i -> System.out.println(i));
System.out.println(oi1.orElse(100) + "\t");
System.out.println(oi2.orElse(100) + "\t");
'Java-Spring > 열혈 Java 프로그래밍' 카테고리의 다른 글
[Java] 시각과 날짜의 처리 (0) | 2023.08.18 |
---|---|
[Java] 스트림 (0) | 2023.08.18 |
[Java] 네스티드 클래스와 람다 (0) | 2023.08.15 |
[Java] 열거형, 가변 인자 그리고 어노테이션 (0) | 2023.08.14 |
[Java] 컬렉션 프레임워크 (0) | 2023.08.13 |