#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์ ์ฌ๋ฌ๊ฐ๋ก ์ชผ๊ฐ ํ์ดํ๋ผ์ด๋์ ์ฝ๊ฒํ๊ธฐ ์ํด ๋ง๋ค์ด์ง ๋ฉ์๋์ด๋ค.
Spliterator์ ์๊ฐ ๋ฐ ํ์ฉ (in JDK 1.8)
JDK 1.8 ์์ ์ถ๊ฐ๋ Stream API ์ค Spliterator ์ธํฐํ์ด์ค๋ฅผ ํ์ตํ ๋ด์ฉ์ ๋ํด ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
jistol.github.io
// 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
#11 Stream API
์คํธ๋ฆผ์ ์ฌ์ฉํ๋๋ฐ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก 1. Stream ์์ฑ 2. ์ค๊ฐ์ฐ์ฐ 3. ์ต์ข ์ฐ์ฐ(Terminal Op) ์ผ๋ก ๋๋ ์ ์๋ค. # Stream API ํน์ง Java์ ๋ชจ๋ Collection์ ๋ด๋ถ์ stream()์ด๋ผ๋ ๊ธฐ๋ณธ ๋ฉ์๋๊ฐ ์กด์ฌํ๋ค. ์ปฌ..
jiwondev.tistory.com
# 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