๋๋ค ํํ์์ ์ต๋ช ํด๋์ค(Anonymous Class)์ฒ๋ผ ์ด๋ฆ์ด ์๋ ํจ์๋ฉด์ ๋ฉ์๋๋ฅผ ์ธ์๋ก ์ ๋ฌํ ์ ์๋ค. ๋๋ค ํํ์์๋ ์ด๋ฆ์ ์์ง๋ง, Parameter List, Body, Return Type, ๋ฐ์ํ ์ ์๋ Exception List๋ฅผ ํฌํจํ ์ ์๋ค.
-- ๊ธฐ์กด
Comparator<Apple> byWeight = new Comparator<Apple>() {
public int compare(Apple a1, Apple a2) {
return a1.getWeight().compareTo(a2.getWeight());
}
}
-- ๋๋ค ํํ์
Comparator<Apple> byWeight = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());
์ ์ฒ๋ผ ๋๋ค ํํ์์ ์ธ ๋ถ๋ถ์ผ๋ก ๊ตฌ์ฑ๋๋ค.
1. ํ๋ผ๋ฏธํฐ ๋ฆฌ์คํธ
2. ํ์ดํ
3. ๋๋ค ๋ฐ๋
ํจ์ํ ์ธํฐํ์ด์ค๋ ์ ํํ ํ๋์ ์ถ์ ๋ฉ์๋๋ฅผ ์ง์ ํ๋ ์ธํฐํ์ด์ค๋ค. ํจ์ํ ์ธํฐํ์ด์ค์๋ @FunctionalInterface ์ด๋ ธํ ์ด์ ์ด ์ถ๊ฐ๋์ด ์์ผ๋ฉฐ, ํจ์ํ ์ธํฐํ์ด์ค์์ ๊ฐ๋ฆฌํจ๋ค.
์๋ฐ API์ ํจ์ํ ์ธํฐํ์ด์ค๋ก Predicate, Comparator, Runnable ๋ฑ์ด ์๋ค.
// java.util.function.Predicate
public interface Predicate<T> {
boolean test(T t);
}
// java.util.Comparator
public interface Comparator<T> {
int compare(T o1, T o2);
}
// java.lang.Runnable
public interface Runnable {
void run();
}
// java.awt.event.ActionListener
public interface ActionListener extends EventListener {
void actionPerformed(ActionEvent e);
}
// java.util.concurrent.Callable
public interface Callable<V> {
V call() throws Exception;
}
// java.security.PrivilegedAction
public interface PrivilegedAction<T> {
T run();
}
๋๋ค ํํ์์ผ๋ก ํจ์ํ ์ธํฐํ์ด์ค์ ์ถ์ ๋ฉ์๋ ๊ตฌํ์ ์ง์ ์ ๋ฌํ ์ ์์ผ๋ฉฐ ์ ์ฒด ํํ์์ ํจ์ํ ์ธํฐํ์ด์ค์ ์ธ์คํด์ค๋ก ์ทจ๊ธํ ์ ์๋ค.
// ํจ์ํ ์ธํฐํ์ด์ค Runnable
Runnable r = () -> System.out.println("Hello World!");
r.run();
// ํจ์ํ ์ธํฐํ์ด์ค Callable
public Callable<String> fetch() {
return () -> "Tricky example ;-)";
}
System.out.println(a1.fetch().call());
ํจ์ํ ์ธํฐํ์ด์ค์ ์ถ์ ๋ฉ์๋ ์๊ทธ๋์ฒ๋ฅผ ํจ์ ๋์คํฌ๋ฆฝํฐ๋ผ๊ณ ํ๋ค. ๋ค์ํ ๋๋ค ํํ์์ ์ฌ์ฉํ๋ ค๋ฉด ๊ณตํต์ ํจ์ ๋์คํฌ๋ฆฝํฐ๋ฅผ ๊ธฐ์ ํ๋ ํจ์ํ ์ธํฐํ์ด์ค ์งํฉ์ด ํ์ํ๋ค.
test๋ผ๋ ์ถ์ ๋ฉ์๋๋ฅผ ์ ์ํ๋ฉฐ ์ ๋ค๋ฆญ ํ์ T์ ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ์ boolean์ ๋ฐํํ๋ค.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public <T> List<T> filter(List<T> list, Predicate<T> p) {
List<T> results = new ArrayList<>();
for (T t : list) {
if (p.test(t)) {
results.add(t);
}
}
return results;
}
List<String> nonEmpty = filter(listOfStrings, (String s) -> !s.isEmpty());
accept๋ผ๋ ์ถ์๋ฉ์๋๋ฅผ ์ ์ํ๋ฉฐ ์ ๋ค๋ฆญ ํ์ T์ ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ์ void๋ฅผ ๋ฐํํ๋ค.
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
public <T> void forEach(List<T> list, Consumer<T> c) {
for (T t : list) {
c.accept(t);
}
}
forEach(Arrays.asList(1, 2, 3, 4, 5), (Integer i) -> System.out.println(i));
apply๋ผ๋ ์ถ์๋ฉ์๋๋ฅผ ์ ์ํ๋ฉฐ ์ ๋ค๋ฆญ ํ์ T์ ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ์์ ์ ๋ค๋ฆญ ํ์ R ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
public <T, R> List<R> map(List<T> list, Function<T, R> f) {
List<R> result = new ArrayList<>();
for (T t : list) {
result.add(f.apply(t));
}
return result;
}
List<Integer> list = map(Arrays.asList("lambdas", "in", "action"), (String s) -> s.length());
์๋ฐ์ ๋ชจ๋ ํ์์ ์ฐธ์กฐํ ์๋๋ฉด ๊ธฐ๋ณธํ์ ํด๋นํ๋๋ฐ, ์ด์ ์ ์ดํด๋ณธ ์ธ ๊ฐ์ ์ ๋ค๋ฆญ ํจ์ํ ์ธํฐํ์ด์ค Predicate<T>, Consumer<T>, Function<T, R>์ ์ฐธ์กฐํ๋ง ์ฌ์ฉํ ์ ์๋ค. Java์์๋ ๊ธฐ๋ณธํ๊ณผ ์ฐธ์กฐํ ๊ฐ์ ์๋ ๋ณํ (Auto Boxing/Unboxing) ์ ์ง์ํ๋ค.
-- Auto Boxing : int (๊ธฐ๋ณธํ) -> Integer (์ฐธ์กฐํ)
List<Integer> list = new ArrayList<>();
for (int i=300; i<400; i++) {
list.add(i);
}
ํ์ง๋ง, ์ด๋ฌํ Boxing/Unboxing ๊ณผ์ ์์ Boxingํ ๊ฐ์ ๊ธฐ๋ณธํ์ ๊ฐ์ธ๋ Wrapper๋ก์จ Heap์ ์ ์ฅ๋์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ ์๋นํ๋ฉฐ ๊ธฐ๋ณธํ์ ๊ฐ์ ธ์ฌ ๋๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ์ํ๋ค. Java 8์์๋ ๊ธฐ๋ณธํ์ ์ ์ถ๋ ฅ์ผ๋ก ์ฌ์ฉํ๋ ์ํฉ์์ Auto Boxing/Unboxing์ ํผํ ์ ์๋๋ก ํน๋ณํ ํ์์ ํจ์ํ ์ธํฐํ์ด์ค๋ฅผ ์ ๊ณตํ๋ค.
ํจ์ํ ์ธํฐํ์ด์ค | ํจ์ ๋์คํฌ๋ฆฝํฐ | boolean ํนํ | int ํนํ | long ํนํ | double ํนํ |
---|---|---|---|---|---|
Predicate<T> | T -> boolean | IntPredicate | LongPredicate | DoublePredicate | |
Consumer<T> | T -> void | IntConsumer | LongConsumer | DoubleConsumer | |
Function<T, R> | T -> R | IntFunction<R> | LongFunction<R> | DoubleFunction<R> | |
IntToLongFunction | LongToIntFunction | DoubleToIntFunction | |||
IntToDoubleFunction | LongToDoubleFunction | DoubleToLongFunction | |||
ToIntFunction<T> | ToLongFunction<T> | ToDoubleFunction<T> | |||
Supplier<T> | () -> T | BooleanSupplier | IntSupplier | LongSupplier | DoubleSupplier |
UnaryOperator<T> | T -> T | IntUnaryOperator | LongUnaryOperator | DoubleUnaryOperator | |
BinaryOperator<T> | (T, T) -> T | IntBinaryOperator | LongBinaryOperator | DoubleBinaryOperator | |
BiPredicate<T, U> | (T, U) -> boolean | ||||
BiConsumer<T, U> | (T, U) -> void | ObjIntConsumer<T> | ObjLongConsumer<T> | ObjDoubleConsumer<T> | |
BiFunction<T, U, R> | (T, U) -> R | ToIntBiFunction<T, U> | ToLongBiFunction<T, U> | ToDoubleBiFunction<T, U> |
@FunctionalInterface
public interface intPredicate {
boolean test(int t);
}
-- Auto Boxing
Predicate<Integer> oddNumbers = (Integer i) -> i % 2 != 0;
oddNumbers.test(1000);
-- Non Boxing
IntPredicate evenNumbers = (int i) -> i % 2 == 0;
evenNumbers.test(1000);
์ฌ์ฉ ์ฌ๋ก | ๋๋ค ์์ | ํจ์ํ ์ธํฐํ์ด์ค |
---|---|---|
boolean ํํ | (List<String> list) -> list.isEmpty() | Predicate<List<String>> |
Object ์์ฑ | () -> new Apple(10) | Supplier<Apple> |
Object ์๋น | (Apple a) -> System.out.println(a.getWeight()) | Consumer<Apple> |
Object ์ ํ/์ถ์ถ | (String s) -> s.length | Function<String, Integer> |
ToIntFunction<String> | ||
๋ ๊ฐ ์กฐํฉ | (int a, int b) -> a * b | IntUnaryOperator |
๋ ๊ฐ์ฒด ๋น๊ต | (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) | Comparator<Apple> |
BiFunction<Apple, Apple, Integer> | ||
ToIntBiFunction<Apple, Apple> |
์์ ์ฒ๋ฆฌ์ ์ฌ์ฉํ๋ ์ํ ํจํด์ ์์์ ์ด๊ณ (Open), ์ฒ๋ฆฌํ ๋ค์์ (Process), ์์์ ๋ซ๋ (Close) ์์๋ก ์ด๋ฃจ์ด์ง๋ค. ์ด์ ๋น์ทํ๊ฒ ์ฝ๋๋ฅผ ์ด๊ธฐํ/์ค๋น ํ๊ณ (init/ready), ์์
์ ์ํํ ๋ค์์ (work), ์ ๋ฆฌ/๋ง๋ฌด๋ฆฌ ํ๋ (final) ํจํด์ด๋ค.
์ฌ๊ธฐ์ ์์ ์ ์ํํ๋ work์ ๋๋ค์ ๋์ ํ๋ผ๋ฏธํฐํ๋ก ์ ์ฐํ๊ณ ๊ฐ๊ฒฐํ ์ฝ๋๋ฅผ ๊ตฌํํ ์ ์๋ค.
-- ํ์ผ์ ์ฝ๋ ์ฝ๋
-- ๊ธฐ์กด ๋ฐฉ๋ฒ (๋์ : ํ ์ค ์ฝ๊ธฐ)
public String processFile() throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return br.readLine();
}
}
-- 1๋จ๊ณ : ํ์ผ์ ์ฝ์ด์ ์ฒ๋ฆฌํ๋ ๋์์ ํ๋ผ๋ฏธํฐํ (ํจ์ํ ์ธํฐํ์ด์ค)
public interface BufferedReaderProcessor {
String process(BufferedReader b) throws IOException;
}
-- 2๋จ๊ณ : ๋์ ์ ๋ฌ ๋ฐ ์คํ
public String processFile(BufferedReaderProcessor p) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) {
return p.process(br);
}
}
-- 3๋จ๊ณ : ๋๋ค ํํ์
String oneLine = processFile(br -> br.readLine());
String twoLine = processFile(br -> br.readLine() + "\r\n" + br.readLine());
๋ฉ์๋ ์ฐธ์กฐ๋ฅผ ์ด์ฉํ๋ฉด ๊ธฐ์กด์ ๋ฉ์๋ ์ ์๋ฅผ ์ฌํ์ฉํด์ ๋๋ค์ฒ๋ผ ์ ๋ฌํ ์ ์๋ค. ๋ฉ์๋๋ช ์์ ๊ตฌ๋ถ์(::)๋ฅผ ๋ถ์ด๋ ๋ฐฉ์์ผ๋ก ๋ฉ์๋ ์ฐธ์กฐ๋ฅผ ํ์ฉํ ์ ์๋ค.
-- ๊ธฐ์กด
inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
-- Method Reference : java.util.Comparator.comparing ํ์ฉ
inventory.sort(comparing(Apple::getWeight));
1) ์ ์ ๋ฉ์๋ ์ฐธ์กฐ
- Ex) Integer.parseInt ๋ฉ์๋๋ฅผ Integer::parseInt๋ก ํํ
2) ๋ค์ํ ํ์์ ์ธ์คํด์ค ๋ฉ์๋ ์ฐธ์กฐ
- Ex) String์ length ๋ฉ์๋๋ฅผ String::length๋ก ํํ
3) ๊ธฐ์กด ๊ฐ์ฒด์ ์ธ์คํด์ค ๋ฉ์๋ ์ฐธ์กฐ
- Ex) Apple ๊ฐ์ฒด์ getColor ๋ฉ์๋๋ฅผ Apple::getColor๋ก ํํ
์์ฑ์, ๋ฐฐ์ด ์์ฑ์, super ํธ์ถ ๋ฑ์ ์ฌ์ฉํ ์ ์๋ ํน๋ณํ ํ์์ ๋ฉ์๋ ์ฐธ์กฐ๋ ์๋ค. List์ sort ๋ฉ์๋๋ ์ธ์๋ก Comparator๋ฅผ ๊ธฐ๋ํ๋ฉฐ, Comparator๋ (T, T) -> int ๋ผ๋ ํจ์ ๋์คํฌ๋ฆฝํฐ๋ฅผ ๊ฐ๋๋ค.
-- ๊ธฐ์กด : ๋์๋ฌธ์ ๊ตฌ๋ณ์์ด ์ ๋ ฌ
List<String\> str = Arrays.asList("a", "b", "A", "B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
-- Method Reference
str.sort(String::compareToIgnoreCase);
ClassName::new ์ฒ๋ผ ํด๋์ค๋ช ๊ณผ new ํค์๋๋ฅผ ์ด์ฉํ์ฌ ๊ธฐ์กด ์์ฑ์์ ์ฐธ์กฐ๋ฅผ ๋ง๋ค ์ ์๋ค.
-- ๊ธฐ์กด
Supplier<Apple> s = () -> new Apple();
Function<Integer, Apple> f = (weight) -> new Apple(weight);
-- ์์ฑ์ ์ฐธ์กฐ
Supplier<Apple> s = Apple::new;
Function<Integer, Apple> f = Apple::new;
Apple a1 = s.get();
Apple a2 = f.apply(100);
-- ์ฌํ : ์ฌ๋ฌ ๊ฐ์ฒด ์์ฑ
List<Integer> weights = Arrays.asList(7, 3, 4, 10);
List<Apple> apples = map(weights, Apple::new);
public List<Apple> map(List<Integer> list, Function<Integer, Apple> f) {
List<Apple> result = new ArrayList<>();
for (Integer i : list) {
result.add(f.apply(i));
}
return result;
}
Java 8 API์ ํจ์ํ ์ธํฐํ์ด์ค๋ ๋๋ค ํํ์์ ์กฐํฉํ ์ ์๋ ์ ํธ๋ฆฌํฐ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. ์ฌ๋ฌ ๊ฐ์ ๋๋ค ํํ์์ ์กฐํฉํด์ ๋ณต์กํ ๋๋ค ํํ์์ ๋ง๋ค ์ ์๋ค.
1) comparing
๋น๊ต์ ์ฌ์ฉํ ํค๋ฅผ ์ถ์ถํ๋ Function ๊ธฐ๋ฐ์ Comparator๋ฅผ ๋ฐํํ๋ค.
Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
2) reversed
์ญ์์ ์ ์ฉํ๋ Comparator๋ฅผ ๋ฐํํ๋ค.
inventory.sort(comparing(Apple::getWeight).reversed());
3) thenComparing
์ด์ Comparator์ ์ด์ด์ ๋ค์ Comparator๋ฅผ ์ ๋ฌํ๋ค.
inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple::getCountry));
1) negate
ํน์ Predicate๋ฅผ ๋ฐ์ ์ํจ๋ค.
-- ๋นจ๊ฐ ์ฌ๊ณผ Predicate
Predicate<Apple> redApple = e -> "RED".equals(e.getColor());
-- ๋นจ๊ฐ ์ฌ๊ณผ๊ฐ ์๋ Predicate
Predicate<Apple> notRedApple = redApple.negate();
2) and
And ์กฐ๊ฑด์ผ๋ก Predicate๋ฅผ ์ฐ๊ฒฐํ๋ค.
-- ๋นจ๊ฐ ์์ด๋ฉด์ ๋ฌด๊ฑฐ์ด ์ฌ๊ณผ
Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);
3) or
Or ์กฐ๊ฑด์ผ๋ก Prdicate๋ฅผ ์ฐ๊ฒฐํ๋ค.
-- ๋นจ๊ฐ์์ด๋ฉด์ ๋ฌด๊ฑฐ์ด ์ฌ๊ณผ ๋๋ ๋
น์ ์ฌ๊ณผ
Predicate<Apple> redAndHeavyAppleOrGreenApple =
redApple
.and(a -> a.getWeight() > 150)
.or(a -> "GREEN".equals(a.getColor());
1) andThen
์ฃผ์ด์ง Function๋ฅผ ๋จผ์ ์ ์ฉํ ๊ฒฐ๊ณผ๋ฅผ ๋ค๋ฅธ Function์ ์
๋ ฅ์ผ๋ก ์ ๋ฌํ๋ค.
Function<String, String> addHeader = Letter::addHeader;
Function<String, String> transformationPipeline =
addHeader
.andThen(Letter::checkSpelling)
.andThen(Letter::addFooter);
2) compose
์ธ์๋ก ์ฃผ์ด์ง Function๋ฅผ ๋จผ์ ์คํํ ๋ค์์ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ธ๋ถ Function์ ์ธ์๋ก ์ ๋ฌํ๋ค.