6. // 1. 컬렉션 정렬 (익명 클래스 사용)
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
컬렉션 정렬 - 익명 클래스 사용
7. // 1. 컬렉션 정렬 (익명 클래스 사용)
Collections.sort(names, ...);
Collections.sort + a.compareTo(b)
new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
}
8. // 1. 컬렉션 정렬 (익명 클래스 사용)
Collections.sort(names, ...);
Collections.sort + [a.compareTo(b), b.compareTo(a)]
new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
}
new Comparator<String>() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
}
9. 새 쓰레드 생성 - 익명 클래스 사용
// 쓰레드 생성 (익명 클래스 사용)
new Thread(new Runnable() {
@Override public void run() {
System.out.println("I consume memory, therefore i am!"); }
}).start();
10. // 1. 쓰레드 생성 (익명 클래스 사용)
new Thread(...);
new Thread(...) + System.out.println("...");
new Thread(new Runnable() {
@Override public void run() {
System.out.println("I consume memory, therefore i am!"); }
}).start();
11. // 1. 쓰레드 생성 (익명 클래스 사용)
new Thread(...);
new Thread(...) + [System.out.println("..."), DB.write("...")]
new Thread(new Runnable() {
@Override public void run() {
System.out.println("I consume memory, therefore i am!"); }
}).start();
new Thread(new Runnable() {
@Override public void run() {
DB.write("I consume memory, therefore i am!"); }
}).start();
12. 행위 매개변수 사용에 장점
● 고정된 코드 + 행위 매개변수(익명 클래스) 조합을 사용한 다양한 확장
a. 더 일반화된 메서드
b. 더 유연한 인터페이스
c. 코드 중복 제거
13. 요구사항 - 리스트 정렬
● 정수형 리스트 정렬
● 문자열 리스트 정렬
● comparable을 상속받지 않은 객체 정렬
a. 서로 다른 필드를 기준으로 정렬(ex: price, name)
14. public static <T extends Comparable<T>> List<T> sort(List<T> list) {
ArrayList<T> ls = new ArrayList<>(list);
for(int i=0; i<ls.size(); i++) {
int minIndex = i;
for(int j=i+1; j<ls.size(); j++) {
if (ls.get(j).compareTo(ls.get(minIndex)) < 0) {
minIndex = j;
}
}
T tmp = ls.get(i);
ls.set(i, ls.get(minIndex));
ls.set(minIndex, tmp);
}
return ls;
}
sort(Arrays.asList("c", "b", "d", "a"))
sort(Arrays.asList(1, 3, 2, 4))
Generic을 사용한 정렬 구현
comparable을 상속
15. 요구사항 - 리스트 정렬
● 정수형 리스트 정렬
● 문자열 리스트 정렬
● comparable을 상속받지 않은 객체 정렬
a. 서로 다른 필드를 기준으로 정렬(ex: price, name)
16. 정렬 기준을 변경하고 싶다
sort(Arrays.asList(k7, k5, k3, i30), 가격 순으로 정렬)
sort(Arrays.asList(k7, k5, k3, i30), 이름 순으로 정렬)
public class Car {
public String name;
public int price;
public Car(String name, int price) {
this.name = name;
this.price = price;
}
}
17. public static <T extends Comparable<T>> List<T> sort(List<T> list) {
ArrayList<T> ls = new ArrayList<>(list);
for(int i=0; i<ls.size(); i++) {
int minIndex = i;
for(int j=i+1; j<ls.size(); j++) {
if (ls.get(j).compareTo(ls.get(minIndex)) < 0) {
minIndex = j;
}
}
T tmp = ls.get(i);
ls.set(i, ls.get(minIndex));
ls.set(minIndex, tmp);
}
return ls;
}
코드 분석
값 2개를 비교해서
[-1, 0, 1]중 하나를 반환
18. public static <T extends Comparable<T>> List<T> sort(List<T> list) {
ArrayList<T> ls = new ArrayList<>(list);
for(int i=0; i<ls.size(); i++) {
int minIndex = i;
for(int j=i+1; j<ls.size(); j++) {
if (ls.get(j).compareTo(ls.get(minIndex)) < 0) {
minIndex = j;
}
}
T tmp = ls.get(i);
ls.set(i, ls.get(minIndex));
ls.set(minIndex, tmp);
}
return ls;
}
인터페이스로 분리
interface Comparator<T> {
int compare(T a, T b);
}
값 2개를 비교해서
[-1, 0, 1]중 하나를 반환
19. public static <T> List<T> sort(List<T> list, Comparator<T> comp) {
ArrayList<T> ls = new ArrayList<>(list);
for(int i=0; i<ls.size(); i++) {
int minIndex = i;
for(int j=i+1; j<ls.size(); j++) {
if (comp.compare(ls.get(j),ls.get(minIndex) < 0) {
minIndex = j;
}
}
T tmp = ls.get(i);
ls.set(i, ls.get(minIndex));
ls.set(minIndex, tmp);
}
return ls;
}
Comparator 인터페이스를 사용
interface Comparator<T> {
int compare(T a, T b);
}
값 2개를 비교해서
[-1, 0, 1]중 하나를 반환
20. // 2. 이름으로 정렬
sort(cars, new Comparator<Car>() {
@Override public int compare(Car a, Car b) {
return a.name.compareTo(b.name);
}
});
익명 클래스를 사용한 행동 전달
// 1. 가격으로 정렬
sort(cars, new Comparator<Car>() {
@Override public int compare(Car a, Car b) {
return a.price.compareTo(b.price);
}
});
다른 부분
가격/이름 으로 비교
21. 요구사항 - 리스트 정렬
● 정수형 리스트 정렬
● 문자열 리스트 정렬
● comparable을 상속받지 않은 객체 정렬
a. 서로 다른 필드를 기준으로 정렬(ex: price, name)
22. 행위 매개변수 사용에 장점
● 고정된 코드 + 행위 매개변수(코드 블럭) 조합을 사용한 다양한 확장
a. 더 일반화된 메서드
b. 더 유연한 인터페이스
c. 코드 중복 제거
23. 람다가 왜 필요할가?
● 행위 매개변수(코드 블럭) 전달
● 자바8 이전에는 익명 클래스를 사용
29. // 1. 람다 사용 (자바8)
Collections.sort(names, (a, b) -> a.compareTo(b));
// 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.compareTo(b);
}
});
행위 매개변수 전달: 클래스 -> 람다
1:1 대응
30. interface Comparator<T> {
int compare(T a, T b);
}
함수형 인터페이스를 사용한 정렬
// 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.compareTo(b);
}
});
31. // 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.compareTo(b);
}
});
interface Comparator<T> {
int compare(T a, T b);
}
불필요한 객체 생성 제거, 메서드도 1개뿐이니 별도 이름 불필요
객체 이름 제거
메서드 이름 제거
32. // 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.compareTo(b);
}
});
interface Comparator<T> {
int compare(T a, T b);
}
반환 타입과 파라미터 타입도 이미 정해져 있으니 제거
객체 이름 제거
메서드 이름 제거
반환 값 및 파라미터 타입 추론
33. // 1. 람다 사용 (자바8)
Collections.sort(names, (a, b) -> { return a.compareTo(b); });
비슷하다
// 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.compareTo(b);
}
});
34. 람다 문법: (인자 목록) -> { 구문 }
● x -> {return x * 2}
● x -> x * 2
● (int x) -> x + 1
● (int x, int y) -> x + y
● (x, y) -> { System.out.println(x + y) }
● () -> { System.out.println("runnable!"); }
실행문이 1개인 경우 {} 와
return 키워드 생략 가능
35. // 1. 람다 사용 (자바8)
Collections.sort(names, (a, b) -> a.compareTo(b));
익명 클래스 -> 람다
// 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.compareTo(b);
}
});
42. public static <X, Y> void processElements(
Iterable<X> source,
Predicate<X> tester,
Function <X, Y> mapper,
Consumer<Y> block) {
for (X p : source) {
if (tester.test(p)) {
Y data = mapper.apply(p);
block.accept(data);
}
}
}
Target typing
processElements(
roster,
p -> p.getGender() == Person.Sex.MALE // Predicate
p -> p.getEmailAddress(), // Function
email -> System.out.println(email) // Consumer
);
람다 두개 모두 같은 모습이지만
알아서 잘 찾아간다
43. Target typing and Method arguments
predicate가 왜 필요할가?
Function<String, Boolean> isDaewon = s -> "daewon".equals(s);
Predicate<String> isDaewon = s -> "daewon".equals(s);
44. Target typing and Method arguments
interface Runnable {
void run();
}
interface Callable<V> {
V call();
}
void invoke(Runnable r) {
r.run();
}
<T> T invoke(Callable<T> c) {
return c.call();
}
invoke(() -> {});
invoke(() -> "done");
45. Target typing and Method arguments
interface Runnable {
void run();
}
interface Callable<V> {
V call();
}
void invoke(Runnable r) {
r.run();
}
<T> T invoke(Callable<T> c) {
return c.call();
}
invoke(() -> {});
invoke(() -> "done");
반환값을 참고해서
오버로드된 메서드도 잘 찾아간다
47. public static void thread(String msg) {
int tmp = 10;
new Thread( () -> System.out.println(msg) ).start();
}
람다 내부에 선언되지 않은 변수를 참조할 수 있다
● 자유변수: 나를 감싸고 있는 유효 범위 변수
a. String msg, int tmp
자신은 감싸고 있는 유효 범위 변수에 접근 가능
48. public static void thread(String msg) {
new Thread( () -> {
msg = "must be final";
System.out.println(msg) ).start();
}
}
컴파일 에러: 포획된 변수를 수정
포획된 변수는 언제나 final이여야 한다
49. Function<String, Predicate<String>> startsWithFactory = s1 -> {
return (s2) -> s2.indexOf(s1) > -1;
};
Predicate<String> isIncludeGoogle = startsWithFactory.apply("google");
Predicate<String> isIncludeApple = startsWithFactory.apply("apple");
System.out.println(isIncludeApple.test("microsoft.com apple.com")); // true
System.out.println(isIncludeGoogle.test("microsoft.com apple.com")); // false
변수 참조를 활용해서 동적으로 새로운 람다를 생성하는 람다
자신은 감싸고 있는 유효 범위 변수에 접근 가능
50. 미리 정의된 함수형 인터페이스
● 람다를 사용하려면 항상 인터페이스를 필요할가?
● 람다 사용시 새로운 인터페이스를 매번 만들어야 하나?
51. 미리 정의된 함수형 인터페이스
● 람다를 사용하려면 항상 인터페이스를 필요할가? - yes
● 람다 사용시 새로운 인터페이스를 매번 만들어야 하나? - no(반만 yes)
52. Car price price > 3000 sum
cars.stream().map(c -> c.gerPrice()).filter(p -> p > 3000).reduce((a, b) -> a + b));
람다를 위해 3개 인터페이스 필요
interface Function interface Predicate interface BinaryOperator
55. // 디버그 모드에서만 실행
public void debug(String message) {
if (log.isDebugEnabled()) {
log.log(message);
}
}
debug(some.expensive("operation"));
디버그 모드에서만 동작하는 함수
디버그 모드에서는 동작
56. // 디버그 모드에서만 실행
public void debug(String message) {
if (log.isDebugEnabled()) {
log.log(message);
}
}
debug(some.expensive("operation"));
평가 시점
함수 인자는 호출 시점에 평가가 된다
57. // 디버그 모드에서만 실행
public void debug(Consumer<String> consumer) {
if (log.isDebugEnabled()) {
log.log(consumer.accept());
}
}
debug(() -> some.expensive("operation"));
람다로 평가 시점 조절
람다를 사용해서 평가를 뒤로 미룬다
58. // 람다가 없는 경우
debug(some.expensive("operation"));
// 람다 사용
debug(() -> some.expensive("operation"));
호출 하는 쪽이 조금 불편해 졌다
69. withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
System.out.println(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
System.out.println(line);
}
});
interface Consumer<T> {
void accept(T a);
}
익명 클래스 사용
70. withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
System.out.println(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
System.out.println(line);
}
});
interface Consumer<T> {
void accept(T a);
}
중복되는 객체 이름 제거
71. withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
System.out.println(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
System.out.println(line);
}
});
interface Consumer<T> {
void accept(T a);
}
중복되는 메서드 이름 제거
72. withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
System.out.println(line);
}
});
withFile("input.txt", new Consumer<String> {
public void accept(String line) {
db.store(line);
System.out.println(line);
}
});
interface Consumer<T> {
void accept(T a);
}
실제로 다른 부분은 함수 본체
73. interface Consumer<T> {
void accept(T a);
}
람다 사용
withFile("input.txt", line -> db.store);
withFile("input.txt", line -> System.out.println);
withFile("input.txt", line -> {
db.store(line);
System.out.println(line);
});
74. interface Consumer<T> {
void accept(T a);
}
class DBWorker implements Consumer<String> {
public void accept(String line) {
db.store(line);
}
}
class PrintWorker implements Consumer<String> {
public void accept(String line) {
System.out.println(line);
}
};
class DBAndPrintWorker implements Consumer<String> {
public void accept(String line) {
db.store(line);
System.out.println(line);
}
};
// 클래스 사용
withFile("input.txt", new DBWorker());
withFile("input.txt", new PrintWorker());
withFile("input.txt", new DBAndPrintWorker());
불필요 코드가 많이 사라짐
// 람다 사용
withFile("input.txt", line -> db.store);
withFile("input.txt", line -> System.out.println);
withFile("input.txt", line -> {
db.store(line);
System.out.println(line);
});