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, ...);
new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
}
Collections.sort + a.compareTo(b)
8. // 1. 컬렉션 정렬 (익명 클래스 사용)
Collections.sort(names, ...);
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);
}
}
Collections.sort + [a.compareTo(b), 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(new Runnable() {
@Override public void run() {
System.out.println("I consume memory, therefore i am!"); }
}).start();
new Thread(...) + System.out.println("...");
11. // 1. 쓰레드 생성 (익명 클래스 사용)
new Thread(...);
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();
new Thread(...) + [System.out.println("..."), DB.write("...")]
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;
}
interface Comparator<T> {
int compare(T a, T b);
}
값 2개를 비교해서
[-1, 0, 1]중 하나를 반환
Comparator 인터페이스를 사용
20. // 1. 가격으로 정렬
sort(cars, new Comparator<Car>() {
@Override public int compare(Car a, Car b) {
return a.price.compareTo(b.price);
// 2. 이름으로 정렬
sort(cars, new Comparator<Car>() {
@Override public int compare(Car a, Car b) {
return a.name.compareTo(b.name);
}
});
익명 클래스를 사용한 행동 전달
}
});
다른 부분
가격/이름 으로 비교
21. 요구사항 - 리스트 정렬
● 정수형 리스트 정렬
● 문자열 리스트 정렬
● comparable을 상속받지 않은 객체 정렬
a. 서로 다른 필드를 기준으로 정렬(ex: price, name)
22. 행위 매개변수 사용에 장점
● 고정된 코드 + 행위 매개변수(코드 블럭) 조합을 사용한 다양한 확장
a. 더 일반화된 메서드
b. 더 유연한 인터페이스
c. 코드 중복 제거
23. 람다가 왜 필요할가?
● 행위 매개변수(코드 블럭) 전달
● 자바8 이전에는 익명 클래스를 사용
24. 익명 함수
Wikipedia
● 특정 식별자 없이 정의되거나 호출될 수 있는 함수
● 자바에 함수가 있나? -> 람다!
25. (인자 목록) -> { 구문 }
● x -> x + 1
● (x) -> x + 1
● (int x) -> x + 1
● (int x, int y) -> x + y
● (x, y) -> { System.out.println(x + y) }
● () -> { System.out.println("runnable!"); }
람다 문법
29. // 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
return a.compareTo(b);
}
});
// 1. 람다 사용 (자바8)
Collections.sort(names, (a, b) -> 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. 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);
}
});
불필요한 객체 생성 제거, 메서드도 1개뿐이니 별도 이름 불필요
32. 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);
}
});
반환 타입과 파라미터 타입도 이미 정해져 있으니 제거
33. // 1. 익명 클래스 사용 (자바8 이전)
Collections.sort(names, new Comparator<String>() {
@Override public int compare(String a, String b) {
// 1. 람다 사용 (자바8)
Collections.sort(names, (a, b) -> { return a.compareTo(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, new Comparator<String>() {
@Override public int compare(String a, String b) {
// 1. 람다 사용 (자바8)
Collections.sort(names, (a, b) -> a.compareTo(b));
익명 클래스 -> 람다
return a.compareTo(b);
}
});
36. @FunctionalInterface
interface Adder {
int add(int a, int b);
}
????? func = (int a, int b) -> { return a + b };
????? shortFunc = (a, b) -> a + b;
람다 타입
37. @FunctionalInterface
interface Adder {
int add(int a, int b);
}
Adder func = (int a, int b) -> { return a + b };
Adder shortFunc = (a, b) -> a + b;
람다 타입
38. Q: 람다는 단순히 익명 클래스에 문법 치환인가?
A: No! 실제로는 익명 클래스에 비효율을 제거하기 위해서 사용
invoke dynamic
39. interface Comparator<T> {
int compare(T a, T b);
}
@FunctionalInterface
interface Runnable<T> {
함수형 인터페이스 - 추상 메서드를 1개만 들고 있다
void run();
}
???
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. Function<String, Boolean> isDaewon = s -> "daewon".equals(s);
Predicate<String> isDaewon = s -> "daewon".equals(s);
predicate가 왜 필요할가?
Target typing and Method arguments
44. interface Runnable {
Target typing and Method arguments
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. interface Runnable {
반환값을 참고해서
오버로드된 메서드도 잘 찾아간다
Target typing and Method arguments
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));
interface Function interface Predicate interface BinaryOperator
람다를 위해 3개 인터페이스 필요
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);
});