#10 Modern Java (Java8~)
by JiwonDev# ํจ์ํ ์ธํฐํ์ด์ค(๋๋ค)
ํด๋์ค๋ฅผ ํจ์ํ ์ธํฐํ์ด์ค๋ก ๊ตฌํํ๋ฉด ๋๋ค ํํ์์ ์ฌ์ฉ ํ ์ ์๋ค.
- ์ถ์ ๋ฉ์๋๋ฅผ ๋ฑ ํ๋๋ง ๊ฐ์ง๊ณ ์๋ ์ธํฐํ์ด์ค
- SAM (Single Abstract Method, ๋จ ํ๋์ ์ถ์๋ฉ์๋ ์ฌ์ฉ) ์ธํฐํ์ด์ค
- @FuncationInterface ์ ๋
ธํ
์ด์
์ ๊ฐ์ง๊ณ ์๋ ์ธํฐํ์ด์ค ( ์๋๋ผ๋ ์ 2์กฐ๊ฑด์ ๋ง์กฑํ๋ฉด ๋์ผ ์ทจ๊ธ.)
public class Main {
public static void main(String[] args) {
// ์ต๋ช
ํจ์ ๋์ ๋๋ค๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ ๊ฒ ๋ฉ์๋ ๊ตฌํ์ด ๊ฐ๋ฅํ๋ค.
Hello hello = () -> System.out.println("Hello");
hello.doSomething(); // Hello ๊ฐ์ฒด ๋ด๋ถ์ ๋ฉ์๋๋ฅผ ๊ตฌํํ ๊ฒ์์ ์ ์ํ์.
}
}
# ๋ฉ์๋ ๋ ํผ๋ฐ์ค
ํจ์๋ฅผ ์ง์ ๊ตฌํํ์ง์๊ณ , ๋ค๋ฅธ ํจ์๋ฅผ ์ฌ์ฌ์ฉํ์ฌ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ฝ๊ฒ ๋์์ฃผ๋ ๊ธฐ๋ฅ์ด๋ค.
JS์์ ๊ฐ๊ฐ์ ์ ๋ ฅ ๊ฐ์ ์ฝ๋ฐฑํจ์๋ฅผ ์ ์ฉ์์ผ์ฃผ๋ ๋ฌธ๋ฒ(Array.map)๊ณผ ๋น์ทํ๋ค.
์ข ๋ฅ | ์ฌ์ฉ๋ฐฉ๋ฒ | ์์ |
Static ๋ฉ์๋ | ContainingClass :: staticMethodName | Person::compareByAge |
ํน์ ๊ฐ์ฒด(myObject)์ ์ธ์คํด์ค ๋ฉ์๋ | containingObject::instanceMethodName | myApp::appendStrings2 |
ํ์ ๊ฐ์ฒด(Type)์ ์ธ์คํด์ค ๋ฉ์๋ | ContainingType::methodName | String::compareToIgnoreCase String::concat |
์์ฑ์ | ClassName::new | HashSet::new |
์ฝ๋ ์์
Static Method ๋ ํผ๋ฐ์ค ์ฌ์ฉ
// ๋ด๊ฐ ๊ตฌํํ ์ธํฐํ์ด์ค
interface Executable {
void doSomething(String text);
}
// ์ด๋ฏธ ์กด์ฌํ๋ Static ๋ฉ์๋
public static class Printer {
static void printSomething(String text) {
System.out.println(text);
}
}
// ๊ผญ ๋๋ค๋ก ํจ์๋ฅผ ์๋กญ๊ฒ ๋ง๋ค์ง ์๋๋ผ๋, ๊ธฐ์กด์ ์กด์ฌํ๋ ํจ์๋ฅผ ์ด์ฉ ํ ์ ์๋ค.
public static void main(String args[]) {
// Executable exe = text -> Printer.printSomething(text); ์ ๋์ผํ ์ฝ๋
Executable exe2 = Printer::printSomething;
exe2.doSomething("do something");
// ํจ์ํ API๋ฅผ ์ด์ฉํ๋ฉด ๊ฐ์ฒด๋ฅผ ์์ฑํ์ง ์๊ณ ๋ ์ฌ์ฉํ ์ ์๋ค.
Consumer<String> consumer = Printer::printSomething;
consumer.accept("do something");
}
Instance Method ๋ ํผ๋ฐ์ค ์ฌ์ฉ
// ๋ด๊ฐ ๋ง๋ ์์์ ํด๋์ค.
public static class Company {
String name;
public Company(String name) {
this.name = name;
}
// static์ด ์๋ ๋ฉ์๋.
public void printName() {
System.out.println(name);
}
}
public static void main(String args[]) {
List<Company> companies = Arrays.asList(new Company("google"),
new Company("apple"), new Company("samsung"));
//companies.stream().forEach(company -> company.printName());์ ๋์ผํ ์ฝ๋
companies1.stream().forEach(Company::printName);
// ์ด๋ฐ์์ผ๋ก ์์ฉ ํ ์ ์๋ค.
List<String> myCompanies = Arrays.asList("google", "apple", "google", "apple", "samsung");
myCompanies.stream()
.mapToInt(String::length) // ๋๋ค์: company -> company.length()
.forEach(System.out::println);
}
Constructor Method (new, ์์ฑ์ ๋ ํผ๋ฐ์ค) ์ฌ์ฉ
public static class Company {
String name;
public Company(String name) {
this.name = name;
}
public void printName() {
System.out.println(name);
}
}
public static void main(String args[]) {
List<String> companies = Arrays.asList("google", "apple", "google", "apple", "samsung");
// companies.stream() ์ด ์ฝ๋์ ๋์ผํจ.
// .map(name -> new Company(name))
// .forEach(company -> company.printName());
companies.stream()
.map(Company::new)
.forEach(Company::printName);
}
# ํจ์ํ API
์๋ฐ์์๋ ๋ชจ๋ ๋ฉ์๋๋ ๊ฐ์ฒด์์ ์กด์ฌํ๋ค. ๊ทธ๋์ ๋ฉ์๋๋ง ๋ฝ์์ ์ฌ์ฌ์ฉํ๊ธฐ ์ด๋ ค์ด๋ฐ, ์ด๋ฅผ ์ฝ๊ฒ ํ๊ธฐ ์ํด ๋ค์ํ API ํ์ ์ ๊ณตํด์ค๋ค.
// B์คํ ํ ๋ฐํ๊ฐ ์ ๋ฌ, ์ดํ A์คํ ์ฆ ํจ์์ ํฉ์ฑ์ด๋ค.
newFunc = funcA.compose(funcB)
// A์คํ ํ B์คํ. ๊ทธ๋ฅ ๋จ์ํ ์ฐ๋ฌ์ ์คํํ๋ ๊ฒ ๋ฟ์ด๋ค.
newFunc = fucnA.andThen(funcB)
์ข ๋ฅ | ํน์ง | ํจ์ ์กฐํฉ์ฉ ๋ฉ์๋ |
Runnable | ์ธ์์ ๋ฐํ๊ฐ์ด ๋ชจ๋ ์์. ๋จ์ง ์คํ๋ง ๊ฐ๋ฅ | |
Function<T,R> | ์ธ์1๊ฐ, ๋ฐํ1๊ฐ R apply(T t) | andThen, Compose |
BiFunction<T,U,R> | ์ธ์2๊ฐ, ๋ฐํ1๊ฐ R apply(T t, U u) | |
Consumer<T> | ์ธ์๋ง ์์. void accept(T t) | andThen |
Supplier<T> | ๋ฐํ๊ฐ๋ง ์์. T get() | |
Predicate<T> | ๋ฐํ๊ฐ๋ง ์์. boolean test(T t) | And, Or, Negate |
UnaryOperator<T> | Function<T, T> ์ ๋์ผ, ์ธ์์ ๊ฐ์ ์ ๋ค๋ฆญ ํ์ ์ ๋ฐํํจ | |
BinaryOperator<T> | BiFunction<T, T, T> ์ ๋์ผ, ์ ๋ ฅ๊ฐ ๋๊ฐ๋ฅผ ๋ฐ์ ๋์ผํ ํ์ ์ ๋ฆฌํดํจ. | |
Comparator<T> | BiFunction<T,T,Integer> ์ ๋์ผ, ๊ฐ์ฒด๊ฐ์ ๊ฐ ๋น๊ตํ๋๋ฐ ์ฌ์ฉ๋จ (compare) | |
๊ธฐํ ์๋ต | BiCosumer<T,U> BiPredicate<T,U>, ObjIntConsumer, LongSupplier ๋ฑ. |
import java.util.function.DoubleFunction;
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
// ์ด๋ ๊ฒ ํจ์๋ฅผ ์ ์ํ๊ณ apply ๋ฅผ ์ด์ฉํ์ฌ ํจ์๋ฅผ ์คํํฉ๋๋ค.
Function<String, Integer> toStr = str -> Integer.parseInt(str);
Integer result = toStr.apply("10");
// ์
๋ ฅ๊ฐ์ด ๊ฐ์ฒด๊ฐ ์๋๋ผ๋ฉด DoubleFunction, IntFunction ๋ฑ์ ์ฌ์ฉํด๋ ๋ฉ๋๋ค.
DoubleFunction<Integer> func = a -> (int) (a * 10);
System.out.println(func.apply(3.2)); // 32
// ์ด๋ ์์ ์ฝ๋์ ๊ฐ์ต๋๋ค.
Function<Integer,Integer> multiply10 = a -> (int) (a * 10);
// ์ด๋ฐ์์ผ๋ก ํจ์๋ฅผ ํฉ์ฑํด์ ์ฌ์ฉ ํ ์๋ ์์ต๋๋ค.
Function<String,Integer> toStringAndMultiply10 = multiply10.compose(toStr);
System.out.println(toStringAndMultiply10.apply("10")); // 100
}
}
# ์๋ก์ด ์ดํฐ๋ ์ดํฐ (forEach, spliteraor)
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ฐ๋ ์ด ๋ฑ์ฅํ๋ฉด์, ์ด๋ฅผ ์ฝ๊ฒ ์ฌ์ฉํ๊ธฐ์ํด ์๋ก์ด ํํ์ iterator๋ ๋ช๊ฐ์ง ์ถ๊ฐ๋์๋ค.
์ฌ๋ด์ผ๋ก ๋ชจ-๋ํ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ ์ฌ์ฉํ๋๊ฒ ๋ฌด์กฐ๊ฑด ์ข์ ๊ฑด ์๋๋ค. ๋ญ๋ ์ง ์ฅ๋จ์ ์ ์์ผ๋ฏ๋ก ํ์ํ ๋ ์ ์ ํ๊ฒ ์ฌ์ฉํด์ผ ํ๋ค.
List<Integer> list = Arrays.asList(1, 2, 3);
// ๊ธฐ์กด์ forEach๋ฌธ
for (Integer i : list)
System.out.println(i);
for (Integer i : list)
for (int j = 0; j < i; j++)
System.out.println(i * j);
// ์๋ก์ด forEach ๋ฉ์๋. ์ฝ๋ฐฑํจ์๋ฅผ ์ฃผ๋ ๊ฒ ์ฒ๋ผ ์ฌ์ฉํ ์ ์๋ค.
list.forEach(System.out::println);
// ์ด ์ ๋๋ก ๋จ์ํ ์์
์ ํจ์ํ์ ์ฐ์ง์๋ ๊ฒ์ด ๋ ์ข์ ์๋ ์๋ค.
list.forEach(i -> {
IntStream.range(0, i).forEach(j -> {
System.out.println(i * j);
});
});
Split + erator๋ ์ด๋ฆ์์๋ ์ ์ ์๋ฏ์ด, ์์ ์ ์ฌ๋ฌ๊ฐ๋ก ์ชผ๊ฐค ์ ์๋ ์ดํฐ๋ ์ดํฐ๋ผ๊ณ ์ดํดํ๋ฉด ๋๋ค.
Stream์ด ์๊ธฐ๋ฉด์ Stream์ ์ฌ๋ฌ๊ฐ๋ก ์ชผ๊ฐ ํ์ดํ๋ผ์ด๋์ ์ฝ๊ฒํ๊ธฐ ์ํด ๋ง๋ค์ด์ง ๋ฉ์๋์ด๋ค.
// Stream๊ณผ Collection์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก spliterator ๋ฉ์๋๋ฅผ ์ ๊ณตํฉ๋๋ค.
Spliterator<Integer> byStream = IntStream.range(0, 100).spliterator();
Spliterator<Integer> byCollection = (new ArrayList<Integer>()).spliterator();
// ๋ฐฐ์ด์ ๊ฒฝ์ฐ Spliterators ํด๋์ค๋ฅผ ํตํด Spliterator ๊ฐ์ฒด๋ฅผ ์์ฑ ํ ์ ์์ต๋๋ค.
Spliterator<Integer> byArray = Spliterators.spliterator(new int[]{1,2,3,4}, Spliterator.SORTED);
public void trySplit_test() {
// ์ด๋ ๊ฒ ๋ฆฌ์คํธ๋ฅผ ๋ถํ ํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
Spliterator<Integer> origin = IntStream.range(0, 19).spliterator();
Spliterator<Integer> dest = origin.trySplit();
System.out.println(dest.estimateSize()); // ํฌ๊ธฐ๋ฅผ ํ์ธํ๋ ๋ฉ์๋ size is 9
dest.forEachRemaining(System.out::print); // ๋ถํ ๋ iter ์ํ print 0~8
System.out.println(origin.estimateSize()); // size is 10
origin.forEachRemaining(System.out::print); // print 9 ~ 18
}
# Stream
Sequence of elements supporting sequential and parallel aggregate operations
(์ํธ์ค ์์ ๋ฐ ๋ณ๋ ฌ ์์ ์ ๋์์ฃผ๋ ์ฐ์ฐ)
์คํธ๋ฆผ์ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ์ปฌ๋ ์ ์ด ์๋๋ค. ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ์ฌ (=์๋ณธ์ ๋ณ๊ฒฝํ์ง ์๊ณ ) ํจ์ํ์ผ๋ก ์์ ํ ์ ์๊ฒ ๋์์ฃผ๋ API์ด๋ค. ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๋ฐฐ์ ๋ค๋ฉด, ๋ค๋ฅธ ์ธ์ด์์ ๋น์ทํ ๊ธฐ๋ฅ์ ํ๋ฒ์ฏค ๋ดค์ ๊ฒ์ด๋ค.
- ์์ํจ์, Immutable, ์คํธ๋ฆผ์ด ์ฒ๋ฆฌํ๋ ๋ฐ์ดํฐ ์์ค๋ฅผ ๋ณ๊ฒฝํ์ง ์๋๋ค.
- ์คํธ๋ฆผ์ ์ฌ์ฌ์ฉ์ด ๋ถ๊ฐ๋ฅํ๋ค. ์คํธ๋ฆผ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐ์ดํฐ๋ ์ค์ง ํ๋ฒ๋ง ์ฒ๋ฆฌํ๋ค.
- ์ค๊ฐ ์คํผ๋ ์ด์ ์ ๊ทผ๋ณธ์ ์ผ๋ก lazy ํ๋ค.
- lazy ํ๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ ๊ฐ์ด ๋ฌดํ์ด์ฌ๋ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค. (Short Circuit ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ํฌ๊ธฐ๋ฅผ ์ ํ)
- ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ฌ์ฉํ์ฌ ์์ฝ๊ฒ ๋ณ๋ ฌ ์ฒ๋ฆฌํ ์ ์๋ค.
List<String> lang =
Arrays.asList("Java", "Scala", "Groovy", "Python", "Go", "Swift");
lang.stream() // 1. ์คํธ๋ฆผ์ ์์ฑํ๋ค
.sorted() // 2. ์ค๊ฐ ์ฐ์ฐ
.collect(Collectors.toList()); // 3. ์ข
๋ฃ ์ฐ์ฐ
// [Go, Groovy, Java, Python, Scala, Swift]
lang.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
// [Swift, Scala, Python, Java, Groovy, Go]
// ์คํธ๋ฆผ์ ์๋ณธ ๊ฐ์ ์์ ํ์ง ์๋๋ค. ์ฆ ๊ธฐ์กด ๋ฐฐ์ด lang์ ๊ฐ์ ๋ณํ์ง ์์์ ๊ธฐ์ตํ์.
// lang == ["Java", "Scala", "Groovy", "Python", "Go", "Swift"]
์คํธ๋ฆผ์๋ ์ฌ๋ฌ๊ฐ์ ์ค๊ฐ ์ฐ์ฐ(intermediate operation)๊ณผ ํ๋์ ์ข ๋ฃ ์ฐ์ฐ(terminal operation)์ผ๋ก ๊ตฌ์ฑ๋๋ค.
์ค๊ฐ ์ฐ์ฐ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ข ๋ฃ ์ฐ์ฐ์ด ์ค๊ธฐ ์ ๊น์ง ์คํ์ ํ์ง ์๋๋ค. ์ฆ lazyํ๊ฒ ๋์ํ๋ฉฐ ์์ ์ต์ ํ๋ฅผ ์ํด ์ค๊ฐ ์ฐ์ฐ์ ๋์ ์์๋ ๋ฐ๋ ์ ์๋ค.
- ์คํธ๋ฆผ ํ์ดํ๋ผ์ธ
- 0 ๋๋ ๋ค์์ ์ค๊ฐ ์คํผ๋ ์ด์
(intermediate operation)๊ณผ ํ๊ฐ์ ์ข
๋ฃ ์คํผ๋ ์ด์
(terminal operation)์ผ๋ก ๊ตฌ์ฑํ๋ค. - ์คํธ๋ฆผ์ ๋ฐ์ดํฐ ์์ค๋ ์ค์ง ํฐ๋ฏธ๋ ์คํผ๋ค์ด์ ์ ์คํํ ๋์๋ง ์ฒ๋ฆฌํ๋ค.
- 0 ๋๋ ๋ค์์ ์ค๊ฐ ์คํผ๋ ์ด์
(intermediate operation)๊ณผ ํ๊ฐ์ ์ข
๋ฃ ์คํผ๋ ์ด์
- ์ค๊ฐ ์คํผ๋ ์ด์
- ์ค๊ฐ ์ฐ์ฐ์ Stream์ ๋ฆฌํดํ๋ค.
- Stateless / Stateful ์คํผ๋ ์ด์
์ผ๋ก ๋ ์์ธํ๊ฒ ๊ตฌ๋ถํ ์๋ ์๋ค.
(๋๋ถ๋ถ์ Stateless์ง๋ง distinct๋ sorted ์ฒ๋ผ ์ด์ ์ด์ ์์ค ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํด์ผ ํ๋ ์คํผ๋ ์ด์ ์ Stateful ์คํผ๋ ์ด์ ์ด๋ค.) - filter, map, limit, skip, sorted, ...
- ์ข
๋ฃ ์คํผ๋ ์ด์
- Stream์ ๋ฆฌํดํ์ง ์๋๋ค.
- collect, allMatch, count, forEach, min, max, ...
์คํธ๋ง API๋ฅผ ์ฌ์ฉํ์ฌ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ๊ตฌํํ๊ฒ๋๋ฉด, ์ฝ๋์ ๊ฐ๋ ์ฑ์ด ๋์์ง๊ณ ์๋ณธ์ ์์ ํ์ง ์์ผ๋ฉฐ ํจ์ ์คํ์ ์์ด Side-effect๋ฅผ ์์ ์ ์ง๋ณด์๋ฅผ ์ฝ๊ฒ ๋ง๋ค์ด์ค๋ค.
// ๊ธฐ์กด ์ฝ๋
final List<Integer> NUMBERS = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer result = null;
for (final Integer number : NUMBERS) {
if (number > 3 && number < 9) {
final Integer newNumber = number * 2;
if (newNumber > 10) {
result = newNumber;
break;
}
}
}
System.out.println("Imperative Result: " + result);
// Stream ์ฌ์ฉ
final List<Integer> NUMBERS = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println("Functional Result: " +
NUMBERS.stream()
.filter(number -> number > 3)
.filter(number -> number < 9)
.map(number -> number * 2)
.filter(number -> number > 10)
.findFirst()
);
# Stream API
2021.07.12 - [Backend/Java] - #11 Stream API
# Optional API (null๊ฐ ์ฒ๋ฆฌ)
2021.07.12 - [Backend/Java] - #12. Optional API (*Null๊ฐ ์ฒ๋ฆฌ)
'๐ฑBackend > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
#12. Optional API (Null๊ฐ ์ฒ๋ฆฌ) (0) | 2021.07.12 |
---|---|
#11 Stream API (0) | 2021.07.12 |
#8 Java - 3 (์ฐ๋ ๋, IO, ๋คํธ์ํฌ) (0) | 2021.07.09 |
#7 Exception (์์ธ์ฒ๋ฆฌ) (0) | 2021.07.07 |
#6. Java - 2 ํด๋์ค (์์ฑ์์ ) (0) | 2021.07.07 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev