SlideShare ist ein Scribd-Unternehmen logo
1 von 75
Downloaden Sie, um offline zu lesen
나에 첫번째 자바8 람다식 
- @blueiur(twitter)
오! 벌써 새벽 
3시네, 컴퓨 
터 끄고 자야 
겠어! 
한 시간 후 ... 
정대원@blueiur(twitter) 
● like .. 
○ programming language 
○ functionl programming 
○ elixir 
○ scala 
○ ruby
람다 
● 람다 계산법 
● 익명 함수 
● 함수 리터럴 
● 클로저
익명 함수 
Wikipedia 
● 특정 식별자 없이 정의되거나 호출될 수 있는 함수
람다가 왜 필요할가? 
● 행위 매개변수(코드 블럭) 전달
// 1. 컬렉션 정렬 (익명 클래스 사용) 
Collections.sort(names, new Comparator<String>() { 
@Override 
public int compare(String a, String b) { 
return a.compareTo(b); 
} 
}); 
컬렉션 정렬 - 익명 클래스 사용
// 1. 컬렉션 정렬 (익명 클래스 사용) 
Collections.sort(names, ...); 
new Comparator<String>() { 
@Override 
public int compare(String a, String b) { 
return a.compareTo(b); 
} 
} 
Collections.sort + a.compareTo(b)
// 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)]
// 쓰레드 생성 (익명 클래스 사용) 
new Thread(new Runnable() { 
@Override public void run() { 
System.out.println("I consume memory, therefore i am!"); } 
새 쓰레드 생성 - 익명 클래스 사용 
}).start();
// 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("...");
// 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("...")]
행위 매개변수 사용에 장점 
● 고정된 코드 + 행위 매개변수(익명 클래스) 조합을 사용한 다양한 확장 
a. 더 일반화된 메서드 
b. 더 유연한 인터페이스 
c. 코드 중복 제거
요구사항 - 리스트 정렬 
● 정수형 리스트 정렬 
● 문자열 리스트 정렬 
● comparable을 상속받지 않은 객체 정렬 
a. 서로 다른 필드를 기준으로 정렬(ex: price, name)
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을 상속
요구사항 - 리스트 정렬 
● 정수형 리스트 정렬 
● 문자열 리스트 정렬 
● comparable을 상속받지 않은 객체 정렬 
a. 서로 다른 필드를 기준으로 정렬(ex: price, name)
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; 
} 
}
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]중 하나를 반환
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]중 하나를 반환
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 인터페이스를 사용
// 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); 
} 
}); 
익명 클래스를 사용한 행동 전달 
} 
}); 
다른 부분 
가격/이름 으로 비교
요구사항 - 리스트 정렬 
● 정수형 리스트 정렬 
● 문자열 리스트 정렬 
● comparable을 상속받지 않은 객체 정렬 
a. 서로 다른 필드를 기준으로 정렬(ex: price, name)
행위 매개변수 사용에 장점 
● 고정된 코드 + 행위 매개변수(코드 블럭) 조합을 사용한 다양한 확장 
a. 더 일반화된 메서드 
b. 더 유연한 인터페이스 
c. 코드 중복 제거
람다가 왜 필요할가? 
● 행위 매개변수(코드 블럭) 전달 
● 자바8 이전에는 익명 클래스를 사용
익명 함수 
Wikipedia 
● 특정 식별자 없이 정의되거나 호출될 수 있는 함수 
● 자바에 함수가 있나? -> 람다!
(인자 목록) -> { 구문 } 
● 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!"); } 
람다 문법
@Functional Interface 
● 추상 메서드가 1개 뿐인 인터페이스 
● 인터페이스를 함수처럼 사용하자
@FunctionalInterface 
interface Action { 
void run(String param); 
void stop(String param); 
} 
@FunctionalInterface 
interface Runnable() { 
void run(); 
메서드 2개 NO! 
} 메서드 1개 OK!
익명 클래스를 람다로 변환해 주는 IntelliJ
// 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 대응
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); 
함수형 인터페이스를 사용한 정렬 
} 
});
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개뿐이니 별도 이름 불필요
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. 익명 클래스 사용 (자바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); 
} 
});
람다 문법: (인자 목록) -> { 구문 } 
● 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 키워드 생략 가능
// 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); 
} 
});
@FunctionalInterface 
interface Adder { 
int add(int a, int b); 
} 
????? func = (int a, int b) -> { return a + b }; 
????? shortFunc = (a, b) -> a + b; 
람다 타입
@FunctionalInterface 
interface Adder { 
int add(int a, int b); 
} 
Adder func = (int a, int b) -> { return a + b }; 
Adder shortFunc = (a, b) -> a + b; 
람다 타입
Q: 람다는 단순히 익명 클래스에 문법 치환인가? 
A: No! 실제로는 익명 클래스에 비효율을 제거하기 위해서 사용 
invoke dynamic
interface Comparator<T> { 
int compare(T a, T b); 
} 
@FunctionalInterface 
interface Runnable<T> { 
함수형 인터페이스 - 추상 메서드를 1개만 들고 있다 
void run(); 
} 
???
@FunctionalInterface 어노테이션은 붙이는 이유? 
● 컴파일러가 추상 메서드가 2개인 경우 컴파일 오류 발생 
● Javadoc에 @FunctionalInterface 글 추가
Target typing 
● 람다 -> 익명 클래스 변환시 가장 유사한 타입을 찾아가는것
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 
); 
람다 두개 모두 같은 모습이지만 
알아서 잘 찾아간다
Function<String, Boolean> isDaewon = s -> "daewon".equals(s); 
Predicate<String> isDaewon = s -> "daewon".equals(s); 
predicate가 왜 필요할가? 
Target typing and Method arguments
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");
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");
변수 포획
public static void thread(String msg) { 
int tmp = 10; 
new Thread( () -> System.out.println(msg) ).start(); 
} 
람다 내부에 선언되지 않은 변수를 참조할 수 있다 
● 자유변수: 나를 감싸고 있는 유효 범위 변수 
a. String msg, int tmp 
자신은 감싸고 있는 유효 범위 변수에 접근 가능
public static void thread(String msg) { 
new Thread( () -> { 
msg = "must be final"; 
System.out.println(msg) ).start(); 
} 
} 
컴파일 에러: 포획된 변수를 수정 
포획된 변수는 언제나 final이여야 한다
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 
변수 참조를 활용해서 동적으로 새로운 람다를 생성하는 람다 
자신은 감싸고 있는 유효 범위 변수에 접근 가능
미리 정의된 함수형 인터페이스 
● 람다를 사용하려면 항상 인터페이스를 필요할가? 
● 람다 사용시 새로운 인터페이스를 매번 만들어야 하나?
미리 정의된 함수형 인터페이스 
● 람다를 사용하려면 항상 인터페이스를 필요할가? - yes 
● 람다 사용시 새로운 인터페이스를 매번 만들어야 하나? - no(반만 yes)
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개 인터페이스 필요
IntPredicate 
IntSupplier 
IntToDoubleFunction 
IntToLongFunction 
IntUnaryOperator 
LongBinaryOperator 
LongConsumer 
LongFunction<R> 
LongPredicate 
LongSupplier 
LongToDoubleFunction 
LongToIntFunction 
LongUnaryOperator 
ObjDoubleConsumer<T> 
ObjIntConsumer<T> 
ObjLongConsumer<T> 
Predicate<T> 
Supplier<T> 
미리 정의된 함수형 인터페이스 
BiConsumer<T,U> 
BiFunction<T,U,R> 
BinaryOperator<T> 
BiPredicate<T,U> 
BooleanSupplier 
Consumer<T> 
DoubleBinaryOperator 
DoubleConsumer 
DoubleFunction<R> 
DoublePredicate 
DoubleSupplier 
DoubleToIntFunction 
DoubleToLongFunction 
DoubleUnaryOperator 
Function<T,R> 
IntBinaryOperator 
IntConsumer 
IntFunction<R>
지연 연산
// 디버그 모드에서만 실행 
public void debug(String message) { 
if (log.isDebugEnabled()) { 
log.log(message); 
} 
} 
debug(some.expensive("operation")); 
디버그 모드에서는 동작 
디버그 모드에서만 동작하는 함수
// 디버그 모드에서만 실행 
public void debug(String message) { 
if (log.isDebugEnabled()) { 
log.log(message); 
} 
} 
debug(some.expensive("operation")); 
평가 시점 
함수 인자는 호출 시점에 평가가 된다
// 디버그 모드에서만 실행 
public void debug(Consumer<String> consumer) { 
if (log.isDebugEnabled()) { 
log.log(consumer.accept()); 
} 
} 
debug(() -> some.expensive("operation")); 
람다로 평가 시점 조절 
람다를 사용해서 평가를 뒤로 미룬다
// 람다가 없는 경우 
debug(some.expensive("operation")); 
// 람다 사용 
debug(() -> some.expensive("operation")); 
호출 하는 쪽이 조금 불편해 졌다
문제 다시 보기 
● 빌려쓰기 패턴
public void withFile(String fileName) { 
BufferedReader bufferedReader = null; 
try { 
bufferedReader = new BufferedReader(new FileReader(filename)); 
String line = null; 
while ((line = bufferedReader.readLine()) != null) { 
System.out.println(line); 
} 
} catch (IOException ex) { 
ex.printStackTrace(); 
} finally { 
if (bufferedReader != null) 
bufferedReader.close(); 
} 
} 
파일을 한 라인씩 읽어서 STDOUT으로 출력
public void withFile(String fileName) { 
BufferedReader bufferedReader = null; 
try { 
bufferedReader = new BufferedReader(new FileReader(filename)); 
String line = null; 
while ((line = bufferedReader.readLine()) != null) { 
DB.write(line); 
} 
} catch (IOException ex) { 
ex.printStackTrace(); 
} finally { 
if (bufferedReader != null) 
bufferedReader.close(); 
} 
} 
파일을 한 라인씩 읽어서 DB에 저장
public void withFile(String fileName) { 
BufferedReader bufferedReader = null; 
try { 
bufferedReader = new BufferedReader(new FileReader(filename)); 
String line = null; 
while ((line = bufferedReader.readLine()) != null) { 
// 이 부분 외 모두 중복! 
} 
} catch (IOException ex) { 
ex.printStackTrace(); 
} finally { 
if (bufferedReader != null) 
bufferedReader.close(); 
} 
} 
중복 발생! 
현재 라인으로 무엇인가를 처리 
System.out.println(line); 
DB.write(line);
public void withFile(String fileName) { 
BufferedReader bufferedReader = null; 
try { 
bufferedReader = new BufferedReader(new FileReader(filename)); 
String line = null; 
while ((line = bufferedReader.readLine()) != null) { 
// 이 부분 외 모두 중복! 
} 
} catch (IOException ex) { 
ex.printStackTrace(); 
} finally { 
if (bufferedReader != null) 
bufferedReader.close(); 
} 
} 
중복 발생! 
interface Consumer<T> { 
void accept(T a); 
} 
System.out.println(line); 
DB.write(line);
어떤 값을 받아서 소비한다. 
interface Consumer<T> { 
void accept(T a); 
} 
각 라인을 인자로 넘겨 받아서 소비한다 public void accept(String line) { 
System.out.println(line); 
}
interface Consumer 
클래스로 구현 
class DBWorker class PrintWorker class DBAndPrintWorker
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()); 
다양한 종류에 클래스 구현
interface Consumer<T> { 
void accept(T a); 
} 
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); 
} 
메서드 딱 1개! 
함수형 인터페이스
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); 
} 
익명 클래스 사용
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); 
} 
중복되는 객체 이름 제거
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); 
} 
중복되는 메서드 이름 제거
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); 
} 
실제로 다른 부분은 함수 본체
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); 
});
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); 
});
감사합니다. 
- @blueiur(twitter)

Weitere ähnliche Inhalte

Was ist angesagt?

Lambda 란 무엇인가
Lambda 란 무엇인가Lambda 란 무엇인가
Lambda 란 무엇인가Vong Sik Kong
 
자바8강의 2강. Stream API
자바8강의 2강. Stream API자바8강의 2강. Stream API
자바8강의 2강. Stream APISejong Park
 
씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 APIArawn Park
 
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)MIN SEOK KOO
 
Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기rupert kim
 
모델링 연습 리뷰
모델링 연습 리뷰모델링 연습 리뷰
모델링 연습 리뷰beom kyun choi
 
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)MIN SEOK KOO
 
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)MIN SEOK KOO
 
일단 시작하는 코틀린
일단 시작하는 코틀린일단 시작하는 코틀린
일단 시작하는 코틀린Park JoongSoo
 
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)Yongha Yoo
 
자바스크립트 함수
자바스크립트 함수자바스크립트 함수
자바스크립트 함수유진 변
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기Yongha Yoo
 
Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리ETRIBE_STG
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8Sangmin Lee
 

Was ist angesagt? (18)

Lambda 란 무엇인가
Lambda 란 무엇인가Lambda 란 무엇인가
Lambda 란 무엇인가
 
JDK 변천사
JDK 변천사JDK 변천사
JDK 변천사
 
자바8강의 2강. Stream API
자바8강의 2강. Stream API자바8강의 2강. Stream API
자바8강의 2강. Stream API
 
씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API씹고 뜯고 맛보고 즐기는 스트림 API
씹고 뜯고 맛보고 즐기는 스트림 API
 
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
이것이 자바다 Chap.14 람다식 Lambda expression(java)(KOR)
 
Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기Java 8 api :: lambda 이용하기
Java 8 api :: lambda 이용하기
 
6 swift 고급함수
6 swift 고급함수6 swift 고급함수
6 swift 고급함수
 
5 swift 기초함수
5 swift 기초함수5 swift 기초함수
5 swift 기초함수
 
모델링 연습 리뷰
모델링 연습 리뷰모델링 연습 리뷰
모델링 연습 리뷰
 
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
(망작)이것이 자바다 Chap.16 스트림&병렬처리 Stream&parallel processing(java)
 
Just java
Just javaJust java
Just java
 
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
이것이 자바다 Chap.11 기본 API 클래스(java)(KOR)
 
일단 시작하는 코틀린
일단 시작하는 코틀린일단 시작하는 코틀린
일단 시작하는 코틀린
 
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
스위프트, 코틀린과 모던언어의 특징 (Swift, Kotlin and Modern Languages)
 
자바스크립트 함수
자바스크립트 함수자바스크립트 함수
자바스크립트 함수
 
스위프트 성능 이해하기
스위프트 성능 이해하기스위프트 성능 이해하기
스위프트 성능 이해하기
 
Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리Javascript 완벽 가이드 정리
Javascript 완벽 가이드 정리
 
SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8SpringCamp 2013 : About Jdk8
SpringCamp 2013 : About Jdk8
 

Ähnlich wie 나에 첫번째 자바8 람다식 지앤선

2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)JiandSon
 
Ai C#세미나
Ai C#세미나Ai C#세미나
Ai C#세미나Astin Choi
 
Functional programming
Functional programmingFunctional programming
Functional programmingssuserdcfefa
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++KWANGIL KIM
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 명신 김
 
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)JiandSon
 
[아꿈사] The C++ Programming Language 11장 연산자 오버로딩
[아꿈사] The C++ Programming Language 11장 연산자 오버로딩[아꿈사] The C++ Programming Language 11장 연산자 오버로딩
[아꿈사] The C++ Programming Language 11장 연산자 오버로딩해강
 
Java jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_streamJava jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_stream성 남궁
 
[Swift] Functions
[Swift] Functions[Swift] Functions
[Swift] FunctionsBill Kim
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1happychallenge
 

Ähnlich wie 나에 첫번째 자바8 람다식 지앤선 (20)

2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 나의 첫번째 자바8 람다식 (정대원)
 
함수적 사고 2장
함수적 사고 2장함수적 사고 2장
함수적 사고 2장
 
Java stream v0.1
Java stream v0.1Java stream v0.1
Java stream v0.1
 
Java stream v0.1
Java stream v0.1Java stream v0.1
Java stream v0.1
 
Ai C#세미나
Ai C#세미나Ai C#세미나
Ai C#세미나
 
강의자료3
강의자료3강의자료3
강의자료3
 
Functional programming
Functional programmingFunctional programming
Functional programming
 
포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++포트폴리오에서 사용한 모던 C++
포트폴리오에서 사용한 모던 C++
 
불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14 불어오는 변화의 바람, From c++98 to c++11, 14
불어오는 변화의 바람, From c++98 to c++11, 14
 
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)
2014.07.26 KSUG와 지앤선이 함께하는 테크니컬 세미나 - 자바8 람다 나머지 이야기 (박성철)
 
06장 함수
06장 함수06장 함수
06장 함수
 
[아꿈사] The C++ Programming Language 11장 연산자 오버로딩
[아꿈사] The C++ Programming Language 11장 연산자 오버로딩[아꿈사] The C++ Programming Language 11장 연산자 오버로딩
[아꿈사] The C++ Programming Language 11장 연산자 오버로딩
 
6 function
6 function6 function
6 function
 
강의자료 2
강의자료 2강의자료 2
강의자료 2
 
Changes in c++0x
Changes in c++0xChanges in c++0x
Changes in c++0x
 
강의자료4
강의자료4강의자료4
강의자료4
 
Java jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_streamJava jungsuk3 ch14_lambda_stream
Java jungsuk3 ch14_lambda_stream
 
[Swift] Functions
[Swift] Functions[Swift] Functions
[Swift] Functions
 
R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1R 프로그램의 이해와 활용 v1.1
R 프로그램의 이해와 활용 v1.1
 
4. stack
4. stack4. stack
4. stack
 

나에 첫번째 자바8 람다식 지앤선

  • 1. 나에 첫번째 자바8 람다식 - @blueiur(twitter)
  • 2. 오! 벌써 새벽 3시네, 컴퓨 터 끄고 자야 겠어! 한 시간 후 ... 정대원@blueiur(twitter) ● like .. ○ programming language ○ functionl programming ○ elixir ○ scala ○ ruby
  • 3. 람다 ● 람다 계산법 ● 익명 함수 ● 함수 리터럴 ● 클로저
  • 4. 익명 함수 Wikipedia ● 특정 식별자 없이 정의되거나 호출될 수 있는 함수
  • 5. 람다가 왜 필요할가? ● 행위 매개변수(코드 블럭) 전달
  • 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!"); } 람다 문법
  • 26. @Functional Interface ● 추상 메서드가 1개 뿐인 인터페이스 ● 인터페이스를 함수처럼 사용하자
  • 27. @FunctionalInterface interface Action { void run(String param); void stop(String param); } @FunctionalInterface interface Runnable() { void run(); 메서드 2개 NO! } 메서드 1개 OK!
  • 28. 익명 클래스를 람다로 변환해 주는 IntelliJ
  • 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(); } ???
  • 40. @FunctionalInterface 어노테이션은 붙이는 이유? ● 컴파일러가 추상 메서드가 2개인 경우 컴파일 오류 발생 ● Javadoc에 @FunctionalInterface 글 추가
  • 41. Target typing ● 람다 -> 익명 클래스 변환시 가장 유사한 타입을 찾아가는것
  • 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개 인터페이스 필요
  • 53. IntPredicate IntSupplier IntToDoubleFunction IntToLongFunction IntUnaryOperator LongBinaryOperator LongConsumer LongFunction<R> LongPredicate LongSupplier LongToDoubleFunction LongToIntFunction LongUnaryOperator ObjDoubleConsumer<T> ObjIntConsumer<T> ObjLongConsumer<T> Predicate<T> Supplier<T> 미리 정의된 함수형 인터페이스 BiConsumer<T,U> BiFunction<T,U,R> BinaryOperator<T> BiPredicate<T,U> BooleanSupplier Consumer<T> DoubleBinaryOperator DoubleConsumer DoubleFunction<R> DoublePredicate DoubleSupplier DoubleToIntFunction DoubleToLongFunction DoubleUnaryOperator Function<T,R> IntBinaryOperator IntConsumer IntFunction<R>
  • 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")); 호출 하는 쪽이 조금 불편해 졌다
  • 59. 문제 다시 보기 ● 빌려쓰기 패턴
  • 60. public void withFile(String fileName) { BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(filename)); String line = null; while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } } catch (IOException ex) { ex.printStackTrace(); } finally { if (bufferedReader != null) bufferedReader.close(); } } 파일을 한 라인씩 읽어서 STDOUT으로 출력
  • 61. public void withFile(String fileName) { BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(filename)); String line = null; while ((line = bufferedReader.readLine()) != null) { DB.write(line); } } catch (IOException ex) { ex.printStackTrace(); } finally { if (bufferedReader != null) bufferedReader.close(); } } 파일을 한 라인씩 읽어서 DB에 저장
  • 62. public void withFile(String fileName) { BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(filename)); String line = null; while ((line = bufferedReader.readLine()) != null) { // 이 부분 외 모두 중복! } } catch (IOException ex) { ex.printStackTrace(); } finally { if (bufferedReader != null) bufferedReader.close(); } } 중복 발생! 현재 라인으로 무엇인가를 처리 System.out.println(line); DB.write(line);
  • 63. public void withFile(String fileName) { BufferedReader bufferedReader = null; try { bufferedReader = new BufferedReader(new FileReader(filename)); String line = null; while ((line = bufferedReader.readLine()) != null) { // 이 부분 외 모두 중복! } } catch (IOException ex) { ex.printStackTrace(); } finally { if (bufferedReader != null) bufferedReader.close(); } } 중복 발생! interface Consumer<T> { void accept(T a); } System.out.println(line); DB.write(line);
  • 64. 어떤 값을 받아서 소비한다. interface Consumer<T> { void accept(T a); } 각 라인을 인자로 넘겨 받아서 소비한다 public void accept(String line) { System.out.println(line); }
  • 65. interface Consumer 클래스로 구현 class DBWorker class PrintWorker class DBAndPrintWorker
  • 66. 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()); 다양한 종류에 클래스 구현
  • 67. interface Consumer<T> { void accept(T a); } 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); } }); 익명 클래스 사용
  • 68. interface Consumer<T> { void accept(T a); } 메서드 딱 1개! 함수형 인터페이스
  • 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); });