๋ฐฑ๊ธฐ์ ๋์ ๋ ์๋ฐ, Java 8 , ๋ ์๋ฐ, ์ฝ๋๋ฅผ ์กฐ์ํ๋ ๋ค์ํ ๋ฐฉ๋ฒ ๊ฐ์๋ฅผ ์์ฝํ ๋ด์ฉ์
๋๋ค.
Java 8
Java 8
LTS(Long-Term-Support) ๋ฒ์
์ฃผ์ ๊ธฐ๋ฅ
๋๋ค ํํ์, ๋ฉ์๋ ๋ ํผ๋ฐ์ค, ์คํธ๋ฆผ API, Optional ...
Open JDK: Oracle, AdoptOpenJDK, Amazon Corretto, Azul Zulu
.
LTS(Long-Term-Support)
๋น-LTS
์ง์ ๊ธฐ๊ฐ: ๋ฐฐํฌ ์ดํ 6๊ฐ์
(๋ค์ ๋ฒ์ ์ด ๋์ค๋ฉด ์ง์ ์ข
๋ฃ)
LTS
๋ฐฐํฌ ์ฃผ๊ธฐ: 3๋
(๋งค 6๋ฒ์งธ ๋ฐฐํฌํ์ด LTS)
์ง์ ๊ธฐ๊ฐ: 5๋
์ด์
(JDK ์ ๊ณต ๋ฐด๋์ ์ด์ฉํ๋ ์๋น์ค์ ๋ฐ๋ผ ์ฐจ์ด)
์ค์ ์๋น์ค ์ด์ ํ๊ฒฝ์์๋ LTS ๋ฒ์ ๊ถ์ฅ
๋งค๋
3์๊ณผ 9์์ ์ ๋ฒ์ ๋ฐฐํฌ
Functional Interface & Lambda
ํจ์ํ ์ธํฐํ์ด์ค(Functional Interface)
Copy @FunctionalInterface
public interface RunSomething {
void doIt();
}
์ถ์ ๋ฉ์๋๋ฅผ ๋ฑ ํ๋๋ง ๊ฐ์ง๊ณ ์๋ ์ธํฐํ์ด์ค
SAM(Single Abstract Method) ์ธํฐํ์ด์ค
@FuncationInterface ์ ๋
ธํ
์ด์
์ ๊ฐ์ง๊ณ ์๋ ์ธํฐํ์ด์ค
.
๋๋ค ํํ์(Lambda Expressions)
Copy RunSomething runSomething = () -> System.out.println("Hello");
RunSomething2 runSomething2 = number -> number + 10;
runSomething.doIt();
runSomething2.doIt();
ํจ์ํ ์ธํฐํ์ด์ค์ ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
๋ฉ์๋ ๋งค๊ฐ๋ณ์, ๋ฆฌํด ํ์
, ๋ณ์๋ก ๋ง๋ค์ด ์ฌ์ฉ ๊ฐ๋ฅ
.
์๋ฐ์์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ
ํจ์๋ฅผ First class object๋ก ์ฌ์ฉ ๊ฐ๋ฅ
์์ ํจ์(Pure function)
์ฌ์ด๋ ์ดํฉํธ ์์(ํจ์ ๋ฐ์ ์๋ ๊ฐ์ ๋ณ๊ฒฝํ์ง ์์)
์ํ๊ฐ ์์(ํจ์ ๋ฐ์ ์๋ ๊ฐ์ ์ฌ์ฉํ์ง ์์)
๊ณ ์ฐจ ํจ์(Higher-Order Function)
ํจ์๊ฐ ํจ์๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ ์ ์๊ณ , ํจ์ ๋ฆฌํด ๊ฐ๋ฅ
Functional Interface
Java ๊ธฐ๋ณธ ์ ๊ณต ํจ์ํ ์ธํฐํ์ด์ค
Function<T, R>
T ํ์
์ ๋ฐ์์ R ํ์
์ ๋ฆฌํดํ๋ ํจ์ ์ธํฐํ์ด์ค
Copy private final Function<Integer, Integer> plus10 = (num) -> num + 10;
private final Function<Integer, Integer> multiply2 = (num) -> num * 2;
@Test
void apply() throws Exception {
/**
* R apply(T t)
*/
Assertions.assertEquals(11, plus10.apply(1));
}
@Test
void compose() throws Exception {
/**
* Function<V, R> compose(Function<? super V, ? extends T> before)
* multiply2 ์คํ ์ดํ plus10 ์คํ
*/
Function<Integer, Integer> multiply2AndPlus10 = plus10.compose(multiply2);
Assertions.assertEquals(14, multiply2AndPlus10.apply(2)); // (num * 2) + 10
}
@Test
void andThen() throws Exception {
/**
* Function<T, V> andThen(Function<? super R, ? extends V> after)
* plus10 ์คํ ์ดํ multiply2 ์คํ
*/
Function<Integer, Integer> plus10AndMultiply2 = plus10.andThen(multiply2);
Assertions.assertEquals(24, plus10AndMultiply2.apply(2)); // (num + 10) * 2
}
BiFunction<T, U, R>
๋ ๊ฐ์ ๊ฐ(T, U)๋ฅผ ๋ฐ์์ R ํ์
์ ๋ฆฌํดํ๋ ํจ์ ์ธํฐํ์ด์ค
Copy @Test
void apply() throws Exception {
/**
* R apply(T t, U u);
*/
BiFunction<Integer, Integer, Integer> add = (num1, num2) -> num1 + num2;
BiFunction<Integer, Integer, Integer> minus = (num1, num2) -> num1 - num2;
BiFunction<Integer, Integer, Integer> multiple = (num1, num2) -> num1 * num2;
Assertions.assertEquals(15, add.apply(10, 5));
Assertions.assertEquals(5, minus.apply(10, 5));
Assertions.assertEquals(50, multiple.apply(10, 5));
}
Consumer<T>
T ํ์
์ ๋ฐ์์ ์๋ฌด๊ฐ๋ ๋ฆฌํดํ์ง ์๋ ํจ์ ์ธํฐํ์ด์ค
Copy @Test
void accept() throws Exception {
/**
* void accept(T t);
*/
Consumer<Integer> printT = System.out::println;
printT.accept(10); // 10
}
@Test
void andThen() throws Exception {
/**
* Consumer<T> andThen(Consumer<? super T> after)
*/
Consumer<String> printJava = s -> System.out.println(s + "Java ");
Consumer<String> printWorld = s -> System.out.println(s + "World ");;
printJava.andThen(printWorld).accept("Hello"); // HelloJava -> HelloWorld
}
Supplier<T>
T ํ์
์ ๊ฐ์ ์ ๊ณตํ๋ ํจ์ ์ธํฐํ์ด์ค
Copy @Test
void get() throws Exception {
/**
* T get()
*/
Supplier<Integer> get10 = () -> 10;
Assertions.assertEquals(10, get10.get());
}
Predicate<T>
T ํ์
์ ๋ฐ์์ boolean์ ๋ฆฌํดํ๋ ํจ์ ์ธํฐํ์ด์ค
ํจ์ ์กฐํฉ์ฉ ๋ฉ์๋
Copy private final Predicate<Integer> isEven = i -> i % 2 == 0;
private final Predicate<Integer> under10 = i -> i < 10;
@Test
void test() throws Exception {
/**
* boolean test(T t);
*/
Predicate<String> startsWithHello = s -> s.startsWith("hello");
Assertions.assertTrue(startsWithHello.test("hello Aaron"));
Assertions.assertTrue(isEven.test(8));
}
@Test
void and() throws Exception {
/**
* Predicate<T> and(Predicate<? super T> other)
*/
Assertions.assertTrue(isEven.and(under10).test(4));
Assertions.assertFalse(isEven.and(under10).test(12));
}
@Test
void or() throws Exception {
/**
* Predicate<T> or(Predicate<? super T> other)
*/
Assertions.assertTrue(isEven.or(under10).test(4));
Assertions.assertTrue(isEven.or(under10).test(12));
Assertions.assertTrue(isEven.or(under10).test(7));
}
@Test
void negate() throws Exception {
/**
* Predicate<T> negate()
*/
Assertions.assertTrue(isEven.negate().test(5));
Assertions.assertTrue(under10.negate().test(17));
Assertions.assertFalse(isEven.negate().test(4));
Assertions.assertFalse(under10.negate().test(5));
}
UnaryOperator<T>
Function<T, R>์ ํน์ํ ํํ(Function ์์)
์
๋ ฅ๊ฐ ํ๋๋ฅผ ๋ฐ์์ ๋์ผํ ํ์
์ ๋ฆฌํดํ๋ ํจ์ ์ธํฐํ์ด์ค
Copy private final UnaryOperator<Integer> plus10 = (num) -> num + 10;
private final UnaryOperator<Integer> multiply2 = (num) -> num * 2;
@Test
void test() throws Exception {
Assertions.assertEquals(11, plus10.apply(1));
Assertions.assertEquals(14, plus10.compose(multiply2).apply(2)); // (num * 2) + 10
Assertions.assertEquals(24, plus10.andThen(multiply2).apply(2)); // (num + 10) * 2
}
BinaryOperator<T>
BiFunction<T, U, R>์ ํน์ํ ํํ
๋์ผํ ํ์
์ ์
๋ ต๊ฐ ๋ ๊ฐ๋ฅผ ๋ฐ์ ๋ฆฌํดํ๋ ํจ์ ์ธํฐํ์ด์ค
Copy @Test
void apply() throws Exception {
/**
* R apply(T t, U u);
*/
BinaryOperator<Integer> add = (num1, num2) -> num1 + num2;
BinaryOperator<Integer> minus = (num1, num2) -> num1 - num2;
BinaryOperator<Integer> multiple = (num1, num2) -> num1 * num2;
Assertions.assertEquals(15, add.apply(10, 5));
Assertions.assertEquals(5, minus.apply(10, 5));
Assertions.assertEquals(50, multiple.apply(10, 5));
}
Lambda
Lambda Expressions
(์ธ์ ๋ฆฌ์คํธ) -> {๋ฐ๋}
.
์ธ์ ๋ฆฌ์คํธ
์ธ์๊ฐ ํ ๊ฐ: (one) ๋๋ one
์ธ์๊ฐ ์ฌ๋ฌ ๊ฐ: (one, two)
์ธ์์ ํ์
์ ์๋ต ๊ฐ๋ฅ(์ปดํ์ผ๋ฌ๊ฐ ์ถ๋ก ํ์ง๋ง ๋ช
์๋ ๊ฐ๋ฅ)
.
๋ฐ๋
ํ์ดํ ์ค๋ฅธ์ชฝ์ ํจ์ ๋ณธ๋ฌธ ์ ์
์ฌ๋ฌ ์ค์ธ ๊ฒฝ์ฐ {} ์ฌ์ฉ
ํ ์ค์ธ ๊ฒฝ์ฐ ๋ฐ๋, return ์๋ต ๊ฐ๋ฅ
.
๋ณ์ ์บก์ฒ(Variable Capture)
๋ก์ปฌ ๋ณ์ ์บก์ฒ
final, effective final ์ธ ๊ฒฝ์ฐ์๋ง ์ฐธ์กฐ ๊ฐ๋ฅ
๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ, concurrency ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ด์ ์ปดํ์ผ๋ฌ๊ฐ ๋ฐฉ์ง
effective final
์๋ฐ 8๋ถํฐ ์ง์ํ๋ ๊ธฐ๋ฅ
final ํค์๋๋ฅผ ์ฌ์ฉํ์ง ์์ง๋ง, ๋ณ๊ฒฝ์ด ์๋ ๋ณ์๋ฅผ ์ต๋ช
ํด๋์ค ๊ตฌํ์ฒด, ๋๋ค์์ ์ฐธ์กฐ ๊ฐ๋ฅ
๋๋ค๋ ์ต๋ช
ํด๋์ค ๊ตฌํ์ฒด์ ๋ฌ๋ฆฌ Shadowing ํ์ง ์์
์ต๋ช
ํด๋์ค๋ ์๋ก์ด ์ค์ฝฅ์ ๋ง๋ค์ง๋ง, ๋๋ค๋ ๋๋ค๋ฅผ ๊ฐ์ธ๊ณ ์๋ ์ค์ฝฅ๊ณผ ๊ฐ์
๋๋ค๋ฅผ ๊ฐ์ผ ์ค์ฝฅ์ ์๋ ๋์ผํ ์ด๋ฆ์ ๋ณ์ ์ ์ ๋ถ๊ฐ
Method Reference
Method References ๋ฅผ ์ฌ์ฉํด์ ๋ฉ์๋, ์์ฑ์ ํธ์ถ๋ฅผ ๋งค์ฐ ๊ฐ๊ฒฐํ๊ฒ ํํ ๊ฐ๋ฅ
Copy @Test
void static_method_reference() throws Exception {
// static method reference(Type::static-method)
UnaryOperator<String> hi = Greeting::hi;
assertEquals("hi aaron", hi.apply("aaron"));
}
@Test
void random_object_instance_method_reference() throws Exception {
String[] names = {"ccc", "aaa", "bbb"};
// random object instance method reference(Type::instance-method)
Arrays.sort(names, String::compareToIgnoreCase);
assertEquals("[aaa, bbb, ccc]", Arrays.toString(names));
}
@Test
void no_arg_constructor_reference() throws Exception {
// no arg constructor reference(Type::new)
Supplier<Greeting> greetingSupplier = Greeting::new;
Greeting greeting = greetingSupplier.get();
// specific object instance method reference(Object-reference::instance-method)
UnaryOperator<String> hello = greeting::hello;
assertEquals("Hello Aaron", hello.apply("Aaron"));
}
@Test
void AllArgsConstructor() throws Exception {
// arg constructor reference(Type::new)
Function<String, Greeting> greetingFunction = Greeting::new;
Greeting greeting = greetingFunction.apply("aaron");
assertEquals("aaron", greeting.getName());
}
@Getter
@NoArgsConstructor
@AllArgsConstructor
private class Greeting {
private String name;
public String hello(String name) {
return "Hello " + name;
}
public static String hi(String name) {
return "hi " + name;
}
}
Interface
Default Methods
Collection interface
Copy /**
* Removes all of the elements of this collection that satisfy the given
* predicate...
*
* @implSpec
* The default implementation traverses all elements of the collection using
* its {@link #iterator}. Each matching element is removed using
* {@link Iterator#remove()}. If the collection's iterator does not
* support removal then an {@code UnsupportedOperationException} will be
* thrown on the first matching element.
*
* ...
*/
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
์ธํฐํ์ด์ค์ ๋ฉ์๋ ์ ์ธ์ด ์๋๋ผ ๊ตฌํ์ฒด๋ฅผ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ
๊ตฌํ ํด๋์ค๋ฅผ ๊นจ๋จ๋ฆฌ์ง ์๊ณ ์ ๊ธฐ๋ฅ ์ถ๊ฐ ๊ฐ๋ฅ
Default Methods ๋ ๊ตฌํ์ฒด ๋ชจ๋ฅด๊ฒ ์ถ๊ฐ๋ ๊ธฐ๋ฅ์ผ๋ก ๋ฆฌ์คํฌ ์กด์ฌ
์ปดํ์ผ ์๋ฌ๋ ์๋์ง๋ง ๊ตฌํ์ฒด์ ๋ฐ๋ผ ๋ฐํ์ ์๋ฌ(ex. NPE) ๋ฐ์ ๊ฐ๋ฅ
๋ฐ๋์ ๋ฌธ์ํ ํ์(@implSpec ์ฌ์ฉ)
ํ์ ์ ๊ตฌํ์ฒด๊ฐ ์ฌ์ ์
Object ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ(equals, hasCode)์ ๊ธฐ๋ณธ ๋ฉ์๋๋ก ์ ๊ณต ๋ถ๊ฐ
๋ณธ์ธ์ด ์์ ํ ์ ์๋ ์ธํฐํ์ด์ค์๋ง ๊ธฐ๋ณธ ๋ฉ์๋ ์ ๊ณต ๊ฐ๋ฅ
์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ๋ ์ธํฐํ์ด์ค์์ ๊ธฐ๋ณธ ๋ฉ์๋๋ฅผ ๋ค์ ์ถ์ ๋ฉ์๋๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ
๊ธฐ๋ณธ ๋ฉ์๋๊ฐ ์ถฉ๋ํ๋ ๊ฒฝ์ฐ ์ง์ ์ค๋ฒ๋ผ์ด๋ฉ ํ์
.
Static Method
ํด๋น ํ์
๊ด๋ จ ํฌํผ, ์ ํธ๋ฆฌํฐ ๋ฉ์๋ ์ ๊ณต ์ ์ ์ฉ
.
Java 8 Default Methods
Iterable ๊ธฐ๋ณธ ๋ฉ์๋
Copy private static final List<String> name = List.of("park", "aaron", "keesun", "whiteship");
@Test
void forEach() throws Exception {
/**
* default void forEach(Consumer<? super T> action)
* - ๋ชจ๋ ์์๊ฐ ์ฒ๋ฆฌ๋๊ฑฐ๋ ์์ธ๊ฐ ๋ฐ์ํ ๋๊น์ง Iterable ๊ฐ ์์์ ๋ํด ์ง์ ๋ ์์
์ํ
*/
name.forEach(System.out::println);
}
@Test
void spliterator() throws Exception {
/**
* default Spliterator<E> spliterator()
* - Creates a Spliterator over the elements described by this Iterable.
*/
Spliterator<String> spliterator1 = name.spliterator();
Spliterator<String> spliterator2 = spliterator1.trySplit();
while(spliterator1.tryAdvance(System.out::println)); // keesun, whiteship
while(spliterator2.tryAdvance(System.out::println)); // park, aaron
}
Collection ๊ธฐ๋ณธ ๋ฉ์๋
parallelStream(), spliterator()
Copy private List<String> name = new ArrayList<>();
@BeforeEach
void beforeEach() {
name.add("park");
name.add("aaron");
name.add("keesun");
name.add("whiteship");
}
@Test
void stream() throws Exception {
/**
* default Stream<E> stream()
*/
long count = name.stream()
.map(String::toUpperCase)
.filter(s -> s.startsWith("A"))
.count();
Assertions.assertEquals(1, count);
}
@Test
void removeIf() throws Exception {
/**
* default Stream<E> stream()
*/
name.removeIf(s -> s.startsWith("w"));
Assertions.assertEquals(3, name.size());
}
Comparator ๊ธฐ๋ณธ ๋ฉ์๋ ๋ฐ ์คํํฑ ๋ฉ์๋
static reverseOrder() / naturalOrder()
static nullsFirst() / nullsLast()
Copy private List<String> name = new ArrayList<>();
@BeforeEach
void beforeEach() {
name.add("park");
name.add("aaron");
name.add("keesun");
name.add("whiteship");
}
@Test
void sort() throws Exception {
/**
* default void sort(Comparator<? super E> c)
*/
// ์์ฐจ์ ๋ ฌ
name.sort(String::compareToIgnoreCase);
// ์ญ์์ ๋ ฌ
Comparator<String> compareToIgnoreCase = String::compareToIgnoreCase;
name.sort(compareToIgnoreCase.reversed());
}
Spliterator ๊ธฐ๋ณธ ๋ฉ์๋
forEachRemaining(Consumer)
Stream
Package java.util.stream
Stream
๋ฐ์ดํฐ๋ฅผ ๋ด๊ณ ์๋ ์ ์ฅ์(์ปฌ๋ ์
)๊ฐ ์๋๋ผ, ์ด๋ ํ ์ฐ์๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ์คํผ๋ ์ด์
๋ค์ ๋ชจ์
์คํธ๋ฆผ ์ฒ๋ฆฌ ์ ๋ฐ์ดํฐ ์๋ณธ์ ๋ณ๊ฒฝํ์ง ์์
์คํธ๋ฆผ์ผ๋ก ์ฒ๋ฆฌํ๋ ๋ฐ์ดํฐ๋ ์ค์ง ํ ๋ฒ๋ง ์ฒ๋ฆฌ
์ค์๊ฐ์ผ๋ก ์คํธ๋ฆผ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ฌ ๊ฒฝ์ฐ ๋ฌดํ ์ฒ๋ฆฌ(Short Circuit ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ์ ํ ๊ฐ๋ฅ)
์ค๊ฐ ์คํผ๋ ์ด์
์ ๊ทผ๋ณธ์ ์ผ๋ก lazy ํน์ฑ์ ๊ฐ์ง
๋ฐ์ดํฐ๊ฐ ๋ฐฉ๋ํ ๊ฒฝ์ฐ parallelStream() ์ผ๋ก ์์ฝ๊ฒ ๋ณ๋ ฌ ์ฒ๋ฆฌ ๊ฐ๋ฅ
์ค๋ ๋ ์์ฑ, ๋ณ๋ ฌ์ฒ๋ฆฌ ํ ์์ง, ์ค๋ ๋ ๊ฐ ์ปจํ
์คํธ ์ค์์นญ ๋ฑ์ ๋น์ฉ์ผ๋ก ๋ฌด์กฐ๊ฑด ๋นจ๋ผ์ง๋ ๊ฑด ์๋
์คํธ๋ฆผ ํ์ดํ๋ผ์ธ
0 ๋๋ ๋ค์์ ์ค๊ฐ ์คํผ๋ ์ด์
๊ณผ ํ ๊ฐ์ ์ข
๋ฃ ์คํผ๋ ์ด์
์ผ๋ก ๊ตฌ์ฑ
์คํธ๋ฆผ์ ๋ฐ์ดํฐ ์์ค๋ ์ค์ง ํฐ๋ฏธ๋ ์คํผ๋ค์ด์
์ ์คํํ ๋์๋ง ์ฒ๋ฆฌ
์ค๊ฐ ์คํผ๋ ์ด์
(intermediate operation)
Stateless / Stateful ์คํผ๋ ์ด์
์ผ๋ก ๋ ์์ธํ๊ฒ ๊ตฌ๋ถ ๊ฐ๋ฅ
๋๋ถ๋ถ Stateless operation
์ด์ ์์ค ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํด์ผ ํ๋ ์คํผ๋ ์ด์
(ex. distinct, sorted)์ Stateful ์คํผ๋ ์ด์
filter, map, limit, skip, sorted ...
์ข
๋ฃ ์คํผ๋ ์ด์
(terminal operation)
collect, allMatch, count, forEach, min, max ...
.
Stream API
StreamTest
ํํฐ๋ง
Copy @Test
@DisplayName("spring ์ผ๋ก ์์ํ๋ ์์
")
void test01() {
/**
* Stream<T> filter(Predicate<? super T> predicate);
*/
List<OnlineClass> springClass = springClasses.stream()
.filter(oc -> oc.getTitle().startsWith("spring"))
.collect(Collectors.toList());
Assertions.assertEquals(5, springClass.size());
}
์คํธ๋ฆผ ๋ณ๊ฒฝ
Copy @Test
@DisplayName("์์
์ด๋ฆ๋ง ๋ชจ์์ ์คํธ๋ฆผ ๋ง๋ค๊ธฐ")
void test03() {
/**
* <R> Stream<R> map(Function<? super T, ? extends R> mapper);
*/
springClasses.stream()
.map(OnlineClass::getTitle)
.forEach(System.out::println);
}
...
@Test
@DisplayName("๋ ์์
๋ชฉ๋ก์ ๋ค์ด ์๋ ๋ชจ๋ ์์
์์ด๋")
void test04() {
/**
* <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
*/
List<OnlineClass> allClasses = aaronEvents.stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
Assertions.assertEquals(8, allClasses.size());
}
์คํธ๋ฆผ ์์ฑ๊ณผ ์ ํ
Copy @Test
@DisplayName("10๋ถํฐ 1์ฉ ์ฆ๊ฐํ๋ ๋ฌด์ ํ ์คํธ๋ฆผ ์ค์์ ์์ 10๊ฐ ๋นผ๊ณ ์ต๋ 10๊ฐ ๊น์ง๋ง")
void test05() {
/**
* public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
* Stream<T> skip(long n);
* Stream<T> limit(long maxSize);
* long count();
*/
long count = Stream.iterate(10, i -> i + 1)
.skip(10)
.limit(10)
.count();
Assertions.assertEquals(10, count);
}
์คํธ๋ฆผ์ ์๋ ๋ฐ์ดํฐ๊ฐ ํน์ ์กฐ๊ฑด์ ๋ง์กฑํ๋์ง ํ์ธ
Copy @Test
@DisplayName("์๋ฐ ์์
์ค Test๊ฐ ๋ค์ด ์๋ ์์
์ด ์๋์ง ํ์ธ")
void test06() {
/**
* boolean anyMatch(Predicate<? super T> predicate);
* boolean allMatch(Predicate<? super T> predicate);
* boolean noneMatch(Predicate<? super T> predicate);
*/
boolean result = javaClasses.stream()
.anyMatch(oc -> oc.getTitle().contains("Test"));
Assertions.assertTrue(result);
}
์คํธ๋ฆผ์ ๋ฐ์ดํฐ ํ๋๋ก ๋ญ์น๊ธฐ
reduce(identity, BiFunction), collect(), sum(), max()
Optional
Class Optional
Copy OptionalInt.of(10);
Optional.empty();
Optional.ofNullable(progress);
NullPointerException ์ ๋ง๋๋ ์ด์
null ์ ๋ฆฌํดํ๊ณ , null ์ฒดํฌ๋ฅผ ๋์น๊ธฐ ๋๋ฌธ
๋ฉ์๋์์ ์์
์ค ํน๋ณํ ์ํฉ์์ ๊ฐ์ ์ ๋๋ก ๋ฆฌํดํ ์ ์๋ ๊ฒฝ์ฐ ์ ํํ ์ ์๋ ๋ฐฉ๋ฒ
์์ธ ๋์ง๊ธฐ (์คํํธ๋ ์ด์ค๋ฅผ ์ฐ์ด๋ค๋ณด๋ ๋น์ผ ๋น์ฉ ๋ฐ์)
null ๋ฆฌํด (ํด๋ผ์ด์ธํธ์ชฝ์์ null ์ฒ๋ฆฌ ํ์)
Optional ๋ฆฌํด (ํด๋ผ์ด์ธํธ์๊ฒ ๋ช
์์ ์ผ๋ก ๋น ๊ฐ์ผ ์๋ ์๋ค๋ ๊ฒ์ ์ ๋ฌํ๊ณ , ๋น ๊ฐ์ธ ๊ฒฝ์ฐ์ ๋ํ ์ฒ๋ฆฌ๋ฅผ ๊ฐ์ )
Optional
ํ ๊ฐ์ ๊ฐ์ด ๋ค์ด์์ ์๋ ์์ ์๋ ์๋ ์ปจ๋ค์ด๋
์ฃผ์์
๋ฆฌํด๊ฐ์ผ๋ก๋ง ์ฌ์ฉ ๊ถ์ฅ
๋ฉ์๋ ๋งค๊ฐ๋ณ์ ํ์
์ผ๋ก ์ฌ์ฉ ์, ๋ฒ๊ฑฐ๋กญ๊ฒ null + optional ์ฒดํฌ ํ์
๋งต์ ํค ํ์
์ผ๋ก ์ฌ์ฉ ์, ๋งต์ ํค๊ฐ ์์ ์๋ ์๋ค๋ ์ํ ์ ๊ณต
์ธ์คํด์ค ํ๋ ํ์
์ผ๋ก ์ฌ์ฉ ์, ํ๋๊ฐ ์์ ์๋ ์๋ค๋ ์ํ ์ ๊ณต
null ๋์ Optional.empty() ๋ฆฌํด ๊ถ์ฅ
Primitive Type Optional ์ ๊ณต
๋ฐ์ฑ, ์ธ๋ฐ์ฑ ๋ฐ์์ ๋ฐฉ์งํ๊ณ , ์ฑ๋ฅ ํฅ์์ ์ํด ์ฌ์ฉ ๊ถ์ฅ
OptionalInt, OptionalLong ...
Collection, Map, Stream Array, Optional์ Opiontal ๋ก ๋ ๋ฒ ๊ฐ์ธ์ง ์๊ธฐ
Tired of Null Pointer Exceptions? Consider Using Java SE 8's "Optional"!
.
Optional API
OptionalTest
Optional ์์ฑ
Copy Optional.of()
Optional.ofNullable()
Optional.empty()
Optional ๊ฐ ํฌํจ ์ฌ๋ถ ํ์ธ
Copy optional.isPresent()
optional.isEmpty() // Java 11 ์ดํ
Optional ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
Copy optional.get(); // ๋น์ด์์ ๊ฒฝ์ฐ NoSuchElementException ์์ธ ๋ฐ์
Optional ์ ๊ฐ์ด ์กด์ฌํ ๊ฒฝ์ฐ ๋์ ์ํ
Copy optional.ifPresent(oc -> System.out.println(oc.getTitle()));
Optional ์ ๊ฐ์ด ์์ ๊ฒฝ์ฐ ๊บผ๋ด๊ณ , ๋ฌด์กฐ๊ฑด ์๋ก์ด ํด๋์ค ์์ฑ
Copy optional.orElseGet(OptionalTest::createNewClass);
Optional ์ ๊ฐ์ด ์์ ๊ฒฝ์ฐ ๊บผ๋ด๊ณ , ์์ผ๋ฉด ์๋ก์ด ํด๋์ค ์ ๊ณต
Copy result.orElseGet(OptionalTest::createNewClass);
Optional ์ ๊ฐ์ด ์์ ๊ฒฝ์ฐ ๊บผ๋ด๊ณ , ์์ผ๋ฉด ์์ธ
Copy assertThrows(NoSuchElementException.class, () -> {
result.orElseThrow();
});
assertThrows(IllegalStateException.class, () -> {
result.orElseThrow(IllegalStateException::new);
});
Optional ๊ฐ์ ํํฐ๋ง
Copy Optional<OnlineClass> jpaClass = result.filter(Predicate.not(OnlineClass::isClosed));
Optional ๊ฐ์ ๋งคํ(๋ณํ)
Copy Optional<Integer> jpaClassId = result.map(OnlineClass::getId);
flatMap(Function): Optional ์์ ๋ค์ด์๋ ์ธ์คํด์ค๊ฐ Optional ์ธ ๊ฒฝ์ฐ ํธ๋ฆฌ
Date & Time API
java 8 ์ ์๋ก์ด ๋ ์ง/์๊ฐ API ๊ฐ ์๊ธด ์ด์
๊ทธ ์ ๊น์ง ์ฌ์ฉํ๋ java.util.Date ํด๋์ค๋ mutable ํ๊ธฐ ๋๋ฌธ์ thead safe ํ์ง ์์
ํด๋์ค ์ด๋ฆ์ด ๋ช
ํํ์ง ์์(Date ์ธ๋ฐ ์๊ฐ๊น์ง ๋ค๋ฃจ๋ ๋ฑ..)
๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ์ฌ์ง๊ฐ ๋ง์(ํ์
์์ ์ฑ์ด ์๊ณ , ์์ด 0๋ถํฐ ์์ํ๋ ๋ฑ..)
๋ ์ง, ์๊ฐ ์ฒ๋ฆฌ๊ฐ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์
์์๋ ๋ณดํต Joda Time ์ฌ์ฉ
java 8 ์์ ์ ๊ณตํ๋ Date-Time API
Design Principles
Clear
: ๋์์ด ๋ช
ํํ๊ณ ์์ ๊ฐ๋ฅ
Fluent
: ์ ์ฐํ ์ธํฐํ์ด์ค ์ ๊ณต. ๋ฉ์๋ ํธ์ถ์ ์ฐ๊ฒฐํ์ฌ ๊ฐ๊ฒฐํจ ์ ๊ณต
Immutable
: ๋ถ๋ณ ๊ฐ์ฒด ์์ฑ, thead safe
Extensible
: ํ์ฅ ๊ฐ๋ฅ
์ฃผ์ API
๊ธฐ๊ณ์ฉ ์๊ฐ(machine time
)๊ณผ ์ธ๋ฅ์ฉ ์๊ฐ(human time
)์ผ๋ก ๊ตฌ๋ถ
๊ธฐ๊ณ์ฉ ์๊ฐ
EPOCK(1970๋
1์ 1์ผ 0์ 0๋ถ 0์ด)๋ถํฐ ํ์ฌ๊น์ง์ ํ์์คํฌํ๋ฅผ ํํ
ํ์์คํฌํ๋ Instant ์ฌ์ฉ
์ธ๋ฅ์ฉ ์๊ฐ
์ฐ๋ฆฌ๊ฐ ํํ ์ฌ์ฉํ๋ ์ฐ,์,์ผ,์,๋ถ,์ด ๋ฑ์ ํํ
ํน์ ๋ ์ง(LocalDate), ์๊ฐ(LocalTime), ์ผ์(LocalDateTime) ์ฌ์ฉ ๊ฐ๋ฅ
๊ธฐ๊ฐ์ ํํํ ๋๋ Duration(์๊ฐ ๊ธฐ๋ฐ)๊ณผ Period(๋ ์ง ๊ธฐ๋ฐ) ์ฌ์ฉ ๊ฐ๋ฅ
DateTimeFormatter ๋ฅผ ์ฌ์ฉํด์ ์ผ์๋ฅผ ํน์ ํ ๋ฌธ์์ด๋ก ํฌ๋งคํ
๊ฐ๋ฅ
์ฐธ๊ณ
.
DateTest.java
๊ธฐ๊ณ์ฉ ์๊ฐ
(machine time) ํํ
UTC(Universal Time Coordinated) == GMT(Greenwich Mean Time)
๋ณดํต ์๊ฐ์ ์ฌ๋ ๊ฒฝ์ฐ ์ฌ์ฉ
Copy Instant instant = Instant.now();
System.out.println(instant); // 2023-09-30T12:44:46.452980Z
System.out.println(instant.atZone(ZoneId.of("UTC"))); // 2023-09-30T12:44:46.452980Z[UTC]
System.out.println(instant.atZone(ZoneId.of("GMT"))); // 2023-09-30T12:45:17.336132Z[GMT]
ZoneId zone = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = instant.atZone(zone);
System.out.println(zone); // Asia/Seoul
System.out.println(zonedDateTime); // 2023-09-30T21:44:46.452980+09:00[Asia/Seoul]
์ธ๋ฅ์ฉ ์๊ฐ
(human time) ํํ
LocalDateTime.of(int, Month, int, int, int, int): ๋ก์ปฌ ํน์ ์ผ์
ZonedDateTime.of(int, Month, int, int, int, int, ZoneId): ํน์ Zone ์ ํน์ ์ผ์
Copy LocalDateTime now = LocalDateTime.now(); // ํ์ฌ ์์คํ
Zone ์ผ์
System.out.println(now); // 2023-09-30T21:57:26.029797
LocalDateTime today = LocalDateTime.of(20023, Month.SEPTEMBER, 30, 0, 0, 0, 0);
System.out.println(today); // +20023-09-30T00:00
ZonedDateTime nowInLosAngeles = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println(nowInLosAngeles); // 2023-09-30T05:57:26.033318-07:00[America/Los_Angeles]
Instant instant = Instant.now();
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("America/Los_Angeles"));
System.out.println(zonedDateTime); // 2023-09-30T05:57:26.034100-07:00[America/Los_Angeles]
๋ ์ง ์ฐ์ฐ
Copy LocalDateTime now = LocalDateTime.now();
LocalDateTime plusDay = now.plus(10, ChronoUnit.DAYS);
LocalDateTime plusMonth = now.plus(2, ChronoUnit.MONTHS);
๊ธฐ๊ฐ ํํ
Copy // Machine Time Duration
Instant now = Instant.now();
Instant plus = now.plus(10, ChronoUnit.SECONDS);
Duration between = Duration.between(now, plus);
System.out.println(between.getSeconds()); // 10
// Human Time Period
LocalDate today = LocalDate.now();
LocalDate christmas = LocalDate.of(2023, Month.DECEMBER, 25);
Period period = Period.between(today, christmas);
System.out.println(period.getMonths()); // 2
Period until = today.until(christmas);
System.out.println(until.getDays()); // 25
Pasing/Formatting
Copy // formatting
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
System.out.println(now.format(formatter)); // 2023/09/30
DateTimeFormatter isoLocalDate = DateTimeFormatter.ISO_LOCAL_DATE;
System.out.println(now.format(isoLocalDate)); // 2023-09-30
// parsing
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
LocalDate parse = LocalDate.parse("2023/09/30", formatter);
System.out.println(parse); // 2023-09-30
๋ ๊ฑฐ์ API ์ง์
GregorianCalendar, Date ํ์
์ ์ธ์คํด์ค๋ฅผ Instant/ZonedDateTime ์ผ๋ก ๋ณํ ๊ฐ๋ฅ
java.util.TimeZone ์์ java.time.ZoneId ๋ก ์ํธ ๋ณํ ๊ฐ๋ฅ
Copy Date date = new Date(); // Sat Sep 30 22:24:04 KST 2023
Instant instant = date.toInstant(); // 2023-09-30T13:24:04.618Z
Date dateFromInstant = Date.from(instant);
GregorianCalendar gregorianCalendar = new GregorianCalendar(); // java.util.GregorianCalendar[time=1696080458867,areFieldsSet=true,areAl...
ZonedDateTime zonedDateTime = gregorianCalendar.toInstant().atZone(ZoneId.systemDefault()); // 2023-09-30T22:27:38.867+09:00[Asia/Seoul]
GregorianCalendar gregorianCalendarFromZonedDateTime = GregorianCalendar.from(zonedDateTime);
ZoneId zoneId = TimeZone.getTimeZone("PST").toZoneId(); // America/Los_Angeles
TimeZone timeZone = TimeZone.getTimeZone(zoneId); // sun.util.calendar.ZoneInfo[id="America/Los_Angeles",of
ZoneId timeZoneFromZonId = timeZone.toZoneId();
CompletableFuture
Java Concurrency
Java Concurrency
Concurrent Software
๋์์ ์ฌ๋ฌ ์์
์ ํ ์ ์๋ ์ํํธ์จ์ด
Java Concurrency Programming
๋ฉํฐํ๋ก์ธ์ฑ(ProcessBuilder)
Java multi-thread Programming
์ฐ๋ ๋ ์ฃผ์ ๊ธฐ๋ฅ(example )
sleep : ํ์ฌ ์ฐ๋ ๋ ๋ฉ์ถ๊ธฐ
๋ค๋ฅธ ์ฐ๋ ๋๊ฐ ์ฒ๋ฆฌํ ์ ์๋๋ก ๊ธฐํ ์ ๊ณต(๋ฝ์ ๋์ง ์์, ๋ฐ๋๋ฝ ๋ฐ์ ๊ฐ๋ฅ)
interrupt : ๋ค๋ฅธ ์ฐ๋ ๋ ๊นจ์ฐ๊ธฐ
๋ค๋ฅธ ์ฐ๋ ๋๋ฅผ ๊นจ์์ interruptedExeption ๋ฐ์
join : ๋ค๋ฅธ ์ฐ๋ ๋ ๋๊ธฐ
๋ค๋ฅธ ์ฐ๋ ๋๊ฐ ๋๋ ๋๊น์ง ๋๊ธฐ
๋ค์์ ์ค๋ ๋๋ฅผ ์ฝ๋ฉ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ด๋ ค์. Execute ์์ฑ.
Executors
High-Level Concurrency Programming
์ฐ๋ ๋๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ ์์
์ ์ ํ๋ฆฌ์ผ์ด์
์์ ๋ถ๋ฆฌํ๊ณ Executors ์๊ฒ ์์
Executors ์ ํ๋ ์ผ
์ฐ๋ ๋ ์์ฑ: ์ ํ๋ฆฌ์ผ์ด์
์ด ์ฌ์ฉํ ์ฐ๋ ๋ ํ์ ๋ง๋ค์ด ๊ด๋ฆฌ
์ฐ๋ ๋ ๊ด๋ฆฌ: ์ฐ๋ ๋ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๊ด๋ฆฌ
์์
์ฒ๋ฆฌ ๋ฐ ์คํ: ์ฐ๋ ๋๋ก ์คํํ ์์
์ ์ ๊ณตํ ์ ์๋ API ์ ๊ณต
์ฃผ์ ์ธํฐํ์ด์ค
Executor
: execute(Runnable)
ExecutorService
: Executor ๋ฅผ ์์ ๋ฐ์ ์ธํฐํ์ด์ค
Callable, Runnable ์คํ, Executor ์ข
๋ฃ
์ฌ๋ฌ Callable ๋์ ์คํ ๋ฑ์ ๊ธฐ๋ฅ ์ ๊ณต
ScheduledExecutorService
: ExecutorService ๋ฅผ ์์ ๋ฐ์ ์ธํฐํ์ด์ค
ํน์ ์๊ฐ ์ดํ ๋๋ ์ฃผ๊ธฐ์ ์ผ๋ก ์์
์คํ
example
Copy /**
* ExecutorService
*
* void shutdown(): ์ด์ ์ ์ ์ถ๋ ์์
์ด ์คํ๋์ง๋ง ์ ์์
์ ํ์ฉ๋์ง ์๋ ์์ฐจ์ ์ข
๋ฃ(Graceful Shutdown)
* List<Runnable> shutdownNow(): ํ์ฌ ์คํ ์ค์ธ ๋ชจ๋ ์์
์ ์ค์งํ๋ ค๊ณ ์๋ํ๊ณ , ๋๊ธฐ ์ค์ธ ์์
์ ์ฒ๋ฆฌ๋ฅผ ์ค์งํ๊ณ , ์คํ ๋๊ธฐ ์ค์ธ ์์
๋ชฉ๋ก์ ๋ฐํ
*/
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(() -> System.out.println("Thread " + Thread.currentThread().getName()));
executorService.shutdown();
/**
* ScheduledExecutorService.schedule
*/
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.schedule(() ->
System.out.println("Thread " + Thread.currentThread().getName()),
5, TimeUnit.SECONDS);
executorService.shutdown();
...
/**
* ScheduledExecutorService.scheduleAtFixedRate
*/
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(() ->
System.out.println("Thread " + Thread.currentThread().getName()),
1, 2, TimeUnit.SECONDS);
Fork/Join ํ๋ ์์ํฌ
ExecutorService ๊ตฌํ์ฒด๋ก ์ฌ์ด ๋ฉํฐ ํ๋ก์ธ์ ํ์ฉ ์ง์
Callable & Future
Callable
Runnable ๊ณผ ์ ์ฌํ์ง๋ง ์์
์ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํด
Future
๋น๋๊ธฐ์ ์ธ ์์
์ ํ์ฌ ์ํ๋ฅผ ์กฐํํ๊ฑฐ๋ ๊ฒฐ๊ณผ ๋ฆฌํด
CallableAndFuture.java
Copy /**
* V get(): ๊ฒฐ๊ณผ ๊ฐ์ ธ์ค๊ธฐ
*
* - Blocking Call: ๊ฐ์ ๊ฐ์ ธ์ฌ ๋๊น์ง ๋๊ธฐ
* - timeout(์ต๋ ๋๊ธฐ ์๊ฐ) ์ค์ ๊ฐ๋ฅ
*/
future.get();
/**
* boolean isDone(): ์์
์ํ ํ์ธ
*/
boolean isDone = future.isDone());
/**
* boolean cancel(boolean mayInterruptIfRunning): ์งํ์ค์ธ ์์
์ interrupt ์์ฒญ์ผ๋ก ์ข
๋ฃ
* - parameter
* - true: ํ์ฌ ์งํ์ค์ธ ์ฐ๋ ๋๋ฅผ interrupt
* - false: ํ์ฌ ์งํ์ค์ธ ์์
์ด ๋๋ ๋๊น์ง ๋๊ธฐ
* - ์ทจ์ ํ์ผ๋ฉด true ๋ชป ํ์ผ๋ฉด false ๋ฆฌํด
* - ์ทจ์ ์ดํ์ get() ์์ฒญ ์ CancellationException ๋ฐ์
*/
boolean cancel = future.cancel(true);
/**
* <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
* - ๋์์ ์คํ(๊ฐ์ฅ ์ค๋ ๊ฑธ๋ฆฌ๋ ์์
๋งํผ ์๊ฐ ์์)
*/
List<Future<String>> futures = executorService.invokeAll(Arrays.asList(hello, the, java));
/**
* <T> T invokeAny(Collection<? extends Callable<T>> tasks)
* - Blocking Call
* - ๋์ ์คํ ์์
์ค ๊ฐ์ฅ ์งง๊ฒ ์์๋๋ ์์
๋งํผ ์๊ฐ ์์
*/
String result = executorService.invokeAny(Arrays.asList(hello, the, java));
CompletableFuture
์๋ฐ์์ ๋น๋๊ธฐ(Asynchronous) ํ๋ก๊ทธ๋๋ฐ์ด ๊ฐ๋ฅํ๋๋ก ์ง์ํ๋ ์ธํฐํ์ด์ค
Future ๋ก๋ ๋น๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ์ด๋์ ๋ ๊ฐ๋ฅํ์ง๋ง, ์ด๋ ค์ด ์์
๋ค์ด ๋ค์ ์กด์ฌ
Future ๋ฅผ ์ธ๋ถ์์ ์๋ฃ ์ฒ๋ฆฌ ๋ถ๊ฐ
cancel(), get() ํ์์์ ์ค์ ์ ๊ฐ๋ฅ
๋ธ๋กํน ์ฝ๋(ex. get())๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์๋ ์์
์ด ๋๋ฌ์ ๋ ์ฝ๋ฐฑ ์คํ ๋ถ๊ฐ
์ฌ๋ฌ Future ์กฐํฉ ๋ถ๊ฐ
ex. ํ์ฌ ์ ๋ณด ์กฐํ ํ ํ์ฌ ์ฐธ์ ํ์ ๋ชฉ๋ก ์กฐํํ๊ธฐ
์์ธ ์ฒ๋ฆฌ์ฉ API ์ ๊ณต X
CompletableFuture
.
๋น๋๊ธฐ๋ก ์์
์คํํ๊ธฐ
Copy /**
* CompletableFuture
* - ์ธ๋ถ์์ Complete ์ธ ๋ช
์์ ์ผ๋ก ์ํฌ ์ ์์
* - Executor ๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ ํ์๊ฐ ์์
*/
CompletableFuture<String> future = new CompletableFuture<>();
future.complete("aaron"); // ํน์ ์๊ฐ ์ด๋ด์ ์๋ต์ด ์์ผ๋ฉด ๊ธฐ๋ณธ ๊ฐ์ผ๋ก ๋ฆฌํดํ๋๋ก ์ค์ ๊ฐ๋ฅ
/**
* public T get() throws InterruptedException, ExecutionException: ๊ฒฐ๊ณผ ๋ฐํ
* public T join(): get() ๊ณผ ๋์ผํ์ง๋ง Unchecked Exception
*/
System.out.println(future.get());
...
/**
* runAsync(): ๋ฆฌํด๊ฐ์ด ์๋ ๋น๋๊ธฐ ์์
*/
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> System.out.println("Hello " + Thread.currentThread().getName()));
future.get();
...
/**
* supplyAsync(): ๋ฆฌํด๊ฐ์ด ์๋ ๋น๋๊ธฐ ์์
*/
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
});
System.out.println(future.get());
.
์ฝ๋ฐฑ ์ ๊ณตํ๊ธฐ
์ฝ๋ฐฑ ์์ฒด๋ฅผ ๋ ๋ค๋ฅธ ์ฐ๋ ๋์์ ์คํ ๊ฐ๋ฅ
Copy /**
* public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
* - ๋ฆฌํด๊ฐ์ ๋ฐ์์ ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ฐ๊พธ๊ณ ๋ฆฌํดํ๋ ์ฝ๋ฐฑ
*/
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).thenApply(s -> s.toUpperCase());
System.out.println(future.get()); // HELLO
...
/**
* public CompletableFuture<Void> thenAccept(Consumer<? super T> action)
* - ๋ฆฌํด๊ฐ์ผ๋ก ๋ ๋ค๋ฅธ ์์
์ ์ฒ๋ฆฌํ๊ณ ๋ฆฌํด์ด ์๋ ์ฝ๋ฐฑ
*/
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).thenAccept(s -> {
System.out.println(s.toUpperCase());
});
future.get(); // HELLO
...
/**
* public CompletableFuture<Void> thenRun(Runnable action)
* - ๋ฆฌํด๊ฐ์ ๋ฐ์ง ์๊ณ ๋ค๋ฅธ ์์
์ ์ฒ๋ฆฌํ๋ ์ฝ๋ฐฑ
*/
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
}).thenRun(() -> {
System.out.println(Thread.currentThread().getName());
});
future.get();
...
/**
* ์ํ๋ Executor(thread-pool)๋ฅผ ์ฌ์ฉํด์ ์คํ ๊ฐ๋ฅ
* - default: ForkJoinPool.commonPool()
*/
ExecutorService executorService = Executors.newFixedThreadPool(4);
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
}, executorService).thenRunAsync(() -> {
System.out.println(Thread.currentThread().getName());
}, executorService);
future.get(); // pool-1-thread-2
executorService.shutdown();
.
์กฐํฉํ๊ธฐ
Copy /**
* public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
* - ์ฐ๊ด์ด ์๋ ๋ ์์
์ด ์๋ก ์ด์ด์ ์คํํ๋๋ก ์กฐํฉ
*/
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
});
CompletableFuture<String> future = hello.thenCompose(CombinationTestApp::getWorld);
System.out.println(future.get());
private static CompletableFuture<String> getWorld(String message) {
return CompletableFuture.supplyAsync(() -> {
System.out.println("World " + Thread.currentThread().getName());
return message + " World";
});
}
...
/**
* public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T, ? super U, ? extends V> fn)
* - ์ฐ๊ด์ด ์๋ ๋ ์์
์ ๋
๋ฆฝ์ ์ผ๋ก ์คํํ๊ณ ๋ ์์
์ด ๋ชจ๋ ์ข
๋ฃ๋์์ ๋ ์ฝ๋ฐฑ ์คํ
*/
CompletableFuture<String> future = hello.thenCombine(world, (h, w) -> h + " " + w);
...
/**
* public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
* - ์ฌ๋ฌ ์์
์ ๋ชจ๋ ์คํํ๊ณ ๋ชจ๋ ์์
๊ฒฐ๊ณผ์ ์ฝ๋ฐฑ ์คํ
*/
CompletableFuture[] futures = {hello, world};
CompletableFuture<List<Object>> results = CompletableFuture.allOf(futures)
.thenApply(v -> Arrays.stream(futures)
.map(CompletableFuture::join)
.collect(Collectors.toList()));
results.get().forEach(System.out::println);
...
/**
* public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
* - ์ฌ๋ฌ ์์
์ค์ ๊ฐ์ฅ ๋นจ๋ฆฌ ์ข
๋ฃ๋ ํ๋์ ๊ฒฐ๊ณผ์ ์ฝ๋ฐฑ ์คํ
*/
CompletableFuture<Void> future = CompletableFuture.anyOf(hello, world).thenAccept(System.out::println);
future.get();
}
.
์์ธ์ฒ๋ฆฌ
Copy /**
* public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn)
* - ์์ธ ์ฒ๋ฆฌ
*/
boolean throwError = true;
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
if (throwError) {
throw new IllegalArgumentException();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).exceptionally(ex -> {
System.out.println(ex);
return "Error!";
});
System.out.println(hello.get());
...
/**
* public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
* - ์ฑ๊ณต ์ผ์ด์ค์ ์์ธ ์ผ์ด์ค ๋ชจ๋ ์ฒ๋ฆฌ
*/
boolean throwError = true;
CompletableFuture<String> hello = CompletableFuture.supplyAsync(() -> {
if (throwError) {
throw new IllegalArgumentException();
}
System.out.println("Hello " + Thread.currentThread().getName());
return "Hello";
}).handle((result, ex) -> {
if (ex != null) {
System.out.println(ex);
return "Error!";
}
return result;
});
System.out.println(hello.get());
Etc..
์ ๋
ธํ
์ด์
์ ๋ณํ
java 8 ์ ๋
ธํ
์ด์
๊ด๋ จ ๋ ๊ฐ์ง ํฐ ๋ณํ
์ ๋
ธํ
์ด์
์ ํ์
์ ์ธ๋ถ
(์ ๋ค๋ฆญ ํ์
, ๋ณ์ ํ์
, ๋งค๊ฐ๋ณ์ ํ์
, ์์ธ ํ์
...)์๋ ์ฌ์ฉ ๊ฐ๋ฅ
TYPE_PARAMETER
: ํ์
๋ณ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅ
TYPE_USE
: TYPE_PARAMETER ํฌํจ ๋ชจ๋ ํ์
์ ์ธ๋ถ์ ์ฌ์ฉ ๊ฐ๋ฅ
Copy static class XXX<@Chicken T> {
/**
* <C> : type parameter
* C : type
*/
public static <@Chicken C> void print(C c){
}
}
์ ๋
ธํ
์ด์
์ค๋ณต ์ฌ์ฉ ๊ฐ๋ฅ
Copy // ์ค๋ณต ์ฌ์ฉํ ์ ๋
ธํ
์ด์
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
@Repeatable(ChickenContainer.class)
public @interface Chicken {
String value();
}
...
// ์ค๋ณต ์ ๋
ธํ
์ด์
์ปจํ
์ด๋
// ์ค๋ณต ์ ๋
ธํ
์ด์
๊ณผ @Retention, @Target ์ด ๊ฐ๊ฑฐ๋ ๋ ๋์ด์ผ ํจ
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface ChickenContainer {
Chicken[] value();
}
...
@Chicken("์๋
")
@Chicken("๋ง๋๊ฐ์ฅ")
public class App {
public static void main(String[] args) {
ChickenContainer chickenContainer = App.class.getAnnotation(ChickenContainer.class);
Arrays.stream(chickenContainer.value()).forEach(c -> {
System.out.println(c.value());
});
}
}
.
Array Parallel Sorting
Arrays.parallelSort()
Fork/Join ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํด์ ๋ฐฐ์ด์ ๋ณ๋ ฌ๋ก ์ ๋ ฌ
ํ๋ ๊ธฐ๋ฅ ์ ๊ณต
๋ณ๋ ฌ ์ ๋ ฌ ์๊ณ ๋ฆฌ๋ฌ
๋ฐฐ์ด์ ๋๋ก ๊ณ์ ์ชผ๊ฐ ํ ํฉ์น๋ฉด์ ์ ๋ ฌ
Copy int size = 1500;
int[] numbers = new int[size];
Random random = new Random();
/**
* Dual-Pivot Quicksort.
* ์๊ณ ๋ฆฌ๋ฌ ํจ์จ์ฑ์ ๋์ผ. ์๊ฐ O(n log(n)) ๊ณต๊ฐ O(n)
*/
IntStream.range(0, size).forEach(i -> numbers[i] = random.nextInt());
long start = System.nanoTime();
Arrays.sort(numbers);
System.out.println("serial sorting took " + (System.nanoTime() - start)); // 629500
IntStream.range(0, size).forEach(i -> numbers[i] = random.nextInt());
start = System.nanoTime();
Arrays.parallelSort(numbers);
System.out.println("parallel sorting took " + (System.nanoTime() - start)); // 400375
.
GC Metaspace
JVM์ ์ฌ๋ฌ ๋ฉ๋ชจ๋ฆฌ ์์ญ ์ค์ PermGen ๋ฉ๋ชจ๋ฆฌ ์์ญ์ด ์์ด์ง๊ณ Metaspace ์์ญ์ด ๋ฑ์ฅ
PermGen (permanent generation)
ํด๋์ค ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ์ ์ฅ์(Heap
์์ญ)
๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ ํ๋ ํฌ๊ธฐ
๋ฅผ ๊ฐ์ง๊ณ ์์
Metaspace
ํด๋์ค ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๋ด๋ ์ ์ฅ์(Heap ์์ญ์ด ์๋๋ผ, Native Memory
์์ญ)
๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ ํ๋ ํฌ๊ธฐ๋ฅผ ๊ฐ์ง๊ณ ์์ง ์์(ํ์ํ ๋งํผ ๊ณ์ ์ฆ๊ฐ
)
java 8 ๋ถํฐ๋ PermGen ๊ด๋ จ ์ต์
์ ๋ฌด์
Copy -XX:MetaspaceSize=N // Metaspace ์ด๊ธฐ ์ฌ์ด์ฆ ์ค์
-XX:MaxMetaspaceSize=N // Metaspace ์ต๋ ์ฌ์ด์ฆ ์ค์
์ฐธ๊ณ
JVM
JAVA, JVM, JDK, JRE
JVM
(Java Virtual Machine)
์๋ฐ ๋ฐ์ดํธ ์ฝ๋(.class)๋ฅผ OS ํนํ๋ ์ฝ๋๋ก ๋ณํ (์ธํฐํ๋ฆฌํฐ์ JIT ์ปดํ์ผ๋ฌ)ํ์ฌ ์คํ
ํน์ ํ๋ซํผ์ ์ข
์์
๋ฐ์ดํธ ์ฝ๋๋ฅผ ์คํํ๋ ํ์ค(JVM ์์ฒด๋ ํ์ค)์ด์ ๊ตฌํ์ฒด(ํน์ ๋ฐด๋๊ฐ ๊ตฌํํ JVM)
JVM ๋ฐด๋: Oracle, Amazon, Azul, ...
JRE
(Java Runtime Environment): JVM + Library
์๋ฐ ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ ์ ์๋๋ก ๊ตฌ์ฑ ๋ ๋ฐฐํฌํ
JVM ๊ณผ ํต์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ ์๋ฐ ๋ฐํ์ ํ๊ฒฝ์์ ์ฌ์ฉํ๋ ํ๋กํผํฐ ์ธํ
์ด๋ ๋ฆฌ์์ค ํ์ผ ๋ณด์
๊ฐ๋ฐ ๊ด๋ จ ๋๊ตฌ๋ ๋ฏธํฌํจ(JDK์์ ์ ๊ณต)
JDK
(Java Development Kit): JRE + Development Tool
์์ค ์ฝ๋๋ฅผ ์์ฑํ ๋ ์ฌ์ฉํ๋ ์๋ฐ ์ธ์ด๋ ํ๋ซํผ์ ๋
๋ฆฝ์
์ค๋ผํด์ ์๋ฐ 11๋ถํฐ๋ JDK ๋ง ์ ๊ณตํ๋ฉฐ JRE ๋ฏธ์ ๊ณต
Write Once Run Anywhere(WORA, ํ ๋ฒ๋ง ์์ฑํ๋ฉด ์ด๋์์๋ ์คํ ๊ฐ๋ฅ)
JAVA
JDK ์ ๋ค์ด์๋ ์๋ฐ ์ปดํ์ผ๋ฌ(javac)๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํธ์ฝ๋(.class)๋ก ์ปดํ์ผ ๊ฐ๋ฅ
์๋ฐ ์ ๋ฃํ ? ์ค๋ผํด์์ ๋ง๋ Oracle JDK 11 ๋ฒ์ ๋ถํฐ ์์ฉ์ผ๋ก ์ฌ์ฉ ์์๋ง ์ ๋ฃ
JVM ์ธ์ด
JVM ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด
Clojure, Groovy, JRuby, Jython, Kotlin, Scala, ...
์ฐธ๊ณ
.
JVM ๊ตฌ์กฐ
Class Loader System
.class ์์ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์ฝ๊ณ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ฅ
loading: ํด๋์ค ์ฝ์ด์ค๋ ๊ณผ์
linking: ๋ ํผ๋ฐ์ค๋ฅผ ์ฐ๊ฒฐํ๋ ๊ณผ์
initialization: static ๊ฐ๋ค์ ์ด๊ธฐํ ๋ฐ ๋ณ์์ ํ ๋น
Memory
Java JVM Run-time Data Areas
Stack Area
์ฐ๋ ๋๋ง๋ค ๋ฐํ์ ์คํ์ ๋ง๋ค๊ณ , ๊ทธ ์์ ๋ฉ์๋ ํธ์ถ์ ์คํ ํ๋ ์์ด๋ผ ๋ถ๋ฅด๋ ๋ธ๋ญ์ผ๋ก ์์
์ฐ๋ ๋๋ฅผ ์ข
๋ฃํ๋ฉด ๋ฐํ์ ์คํ๋ ์๋ฉธ(์ฐ๋ ๋์์๋ง ๊ณต์ )
PC(Program Counter) registers Area
์ฐ๋ ๋๋ง๋ค ์ฐ๋ ๋ ๋ด ํ์ฌ ์คํํ instruction ์์น๋ฅผ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ ์์ฑ(์ฐ๋ ๋์์๋ง ๊ณต์ )
native method stack Area
์ฐ๋ ๋๋ง๋ค ์์ฑ๋๊ณ , native method ํธ์ถ ์ ์ฌ์ฉํ๋ ๋ณ๋์ method stack(์ฐ๋ ๋์์๋ง ๊ณต์ )
heap Area
๊ฐ์ฒด ์ ์ฅ(๊ณต์ ์์)
method Area
ํด๋์ค ์์ค์ ์ ๋ณด(ํด๋์ค ์ด๋ฆ, ํจํค์ง ๊ฒฝ๋ก, ๋ถ๋ชจ ํด๋์ค ์ด๋ฆ, ๋ฉ์๋, ๋ณ์)์ ์ฅ(๊ณต์ ์์)
Execution Engine
์ธํฐํ๋ฆฌํฐ
๋ฐ์ดํธ ์ฝ๋๋ฅผ ํ์ค์ฉ ์คํ
JIT(Just-In-Time) ์ปดํ์ผ๋ฌ
์ธํฐํ๋ฆฌํฐ ํจ์จ์ ๋์ด๊ธฐ ์ํด ์ธํฐํ๋ฆฌํฐ๊ฐ ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ๋ฐ๊ฒฌํ๋ฉด JIT ์ปดํ์ผ๋ฌ๋ก ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ๋ค์ดํฐ๋ธ ์ฝ๋๋ก ๋ณ๊ฒฝ
๊ทธ ๋ค์๋ถํฐ ์ธํฐํ๋ฆฌํฐ๋ ๋ค์ดํฐ๋ธ ์ฝ๋๋ก ์ปดํ์ผ๋ ์ฝ๋๋ฅผ ๋ฐ๋ก ์ฌ์ฉ
GC(Garbage Collector)
๋์ด์ ์ฐธ์กฐ๋์ง ์๋ ๊ฐ์ฒด๋ฅผ ๋ชจ์์ ์ ๋ฆฌ
JNI
(Java Native Interface)
์๋ฐ ์ ํ๋ฆฌ์ผ์ด์
์์ C, C++, ์ด์
๋ธ๋ฆฌ๋ก ์์ฑ๋ ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ ์ ๊ณต
Native ํค์๋๋ฅผ ์ฌ์ฉํ ๋ฉ์๋ ํธ์ถ
Native Method Library
C, C++๋ก ์์ฑ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์ฐธ๊ณ
.
Class Loader System
๋ก๋ฉ, ๋งํฌ, ์ด๊ธฐํ ์์ผ๋ก ์งํ
Loading
ํด๋์ค ๋ก๋๊ฐ .class ํ์ผ์ ์ฝ๊ณ ๊ทธ ๋ด์ฉ์ ๋ฐ๋ผ ์ ์ ํ ๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ ๋ฅผ ๋ง๋ค๊ณ Method Memory ์์ญ์ ์ ์ฅ
Method Memory ์์ญ์ ์ ์ฅํ๋ ๋ฐ์ดํฐ
FQCN(Full Qualified Class Name)
ํด๋์ค/์ธํฐํ์ด์ค/์ด๋
๋ก๋ฉ์ด ๋๋๋ฉด ํด๋น ํด๋์ค ํ์
์ Class ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ Heap ์์ญ์ ์ ์ฅ
ํด๋์ค ๋ก๋๋ ๊ณ์ธต ๊ตฌ์กฐ๋ก ์ด๋ค์ ธ ์์ผ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ์ธ๊ฐ์ง ํด๋์ค ๋ก๋๊ฐ ์ ๊ณต
BootstrapClassLoader
์ต์์ ์ฐ์ ์์๋ฅผ ๊ฐ์ง ํด๋์ค ๋ก๋
JAVA_HOME\lib ์ ์๋ ์ฝ์ด ์๋ฐ API ์ ๊ณต
PlatformClassLoader
JAVA_HOME\lib\ext ํด๋ ๋๋ java.ext.dirs ์์คํ
๋ณ์์ ํด๋นํ๋ ํด๋์ค๋ฅผ ์ฝ์
AppClassLoader
์ ํ๋ฆฌ์ผ์ด์
ํด๋์ค ํจ์ค์์ ํด๋์ค๋ฅผ ์ฝ์
ํด๋์ค ํจ์ค: ์ ํ๋ฆฌ์ผ์ด์
์คํ ์ -classpath ์ต์
๋๋ java.class.path ํ๊ฒฝ ๋ณ์ ๊ฐ์ ํด๋นํ๋ ์์น
์ต์์ ํด๋์ค ๋ก๋๋ถํฐ ํด๋์ค๋ฅผ ์ฐธ์ํ๋๋ฐ ๋ชจ๋ ํด๋์ค ๋ก๋๊ฐ ํด๋์ค๋ฅผ ์ฐพ์ง ๋ชป ํ๋ค๋ฉด ClassNotFoundException ๋ฐ์
Linking
Verify: .class ํ์ผ ํ์์ด ์ ํจํ์ง ์ฒดํฌ
Preparation: ํด๋์ค ๋ณ์(static ๋ณ์)์ ๊ธฐ๋ณธ๊ฐ์ ํ์ํ ๋ฉ๋ชจ๋ฆฌ ์ค๋น
Resolve(optional): ์ฌ๋ณผ๋ฆญ ๋ฉ๋ชจ๋ฆฌ ๋ ํผ๋ฐ์ค๋ฅผ ๋ฉ์๋ ์์ญ์ ์๋ ์ค์ ๋ ํผ๋ฐ์ค๋ก ๊ต์ฒด
Initialization
Static ๋ณ์์ ๊ฐ์ ํ ๋น(static ๋ธ๋ญ์ด ์๋ค๋ฉด ์ด๋ ์คํ)
Bytecode Operation
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ์ธก์
ํ
์คํธ ์ฝ๋๊ฐ ํ์ธํ ์์ค ์ฝ๋ ๋น์จ
.
ํด๋์ค ๋ก๋ฉ ์ ๋ฐ์ดํธ์ฝ๋ ์กฐ์
ํ๋ก๊ทธ๋จ ๋ถ์
์ฝ๋์์ ๋ฒ๊ทธ๋ฅผ ์ฐพ๋ ํด
ํด๋์ค ํ์ผ ์์ฑ
ํน์ API ํธ์ถ ์ ๊ทผ ์ ํ
์ค์นผ๋ผ ๊ฐ์ ์ธ์ด์ ์ปดํ์ผ๋ฌ
๊ทธ๋ฐ์๋ ์๋ฐ ์์ค์ฝ๋๋ฅผ ๊ฑด๋๋ฆฌ์ง ์๊ณ ์ฝ๋ ๋ณ๊ฒฝ์ด ํ์ํ ์ฌ๋ฌ ๊ฒฝ์ฐ์ ์ฌ์ฉ ๊ฐ๋ฅ
ํ๋กํ์ผ๋ฌ: CPU ์ฌ์ฉ๋ฅ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋, Thread ์ ๋ณด ..
์คํ๋ง์ ๋ฐ์ดํธ์ฝ๋ ์กฐ์ ํด ์ฌ์ฉ: ์คํ๋ง ์ปดํฌ๋ํธ ์ค์บ
๋น์ผ๋ก ๋ฑ๋กํ ํ๋ณด ํด๋์ค ์ ๋ณด๋ฅผ ์ฐพ๋๋ฐ ASM ์ฌ์ฉ
ClassPathScanningCandidateComponentProvider -> SimpleMetadataReader
ClassReader, Visitor ๋ฅผ ์ฌ์ฉํด์ ํด๋์ค์ ์๋ ๋ฉํ ์ ๋ณด ์กฐํ
์ฐธ๊ณ
.
๋ฐ์ดํธ์ฝ๋ ์กฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
ByteBuddy
.class ํ์ผ ์์ฒด๋ฅผ ๋ณ๊ฒฝ์ํค๋ ๋ฐฉ๋ฒ
๊ถ์ฅํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
Copy new ByteBuddy().redefine(Moja.class)
.method(named("pullOut")).intercept(FixedValue.value("Rabbit!"))
.make().saveIn(new File("../target/classes/"))
Javassist
ํด๋์ค ๋ก๋๊ฐ ํด๋์ค๋ฅผ ์ฝ์ด์ฌ ๋ javaagent ๋ฅผ ๊ฑฐ์ณ์ ๋ณ๊ฒฝ๋ ๋ฐ์ดํธ์ฝ๋๋ฅผ ์ฝ์ด์ด
premain: ์์ ์ ๋ถ์ด๋ ๋ฐฉ์
agentmain: ๋ฐํ์ ์ค ๋์ ์ผ๋ก ๋ถ์ด๋ ๋ฐฉ์
Copy public static void premain(String agentArgs, Instrumentation inst) {
new AgentBuilder.Default()
.type(ElementMatchers.any())
.transform((builder, typeDescription, classLoader, javaModule) ->
builder.method(named("pullOut")).intercept(FixedValue.value("Rabbit!"))).installOn(inst);
}
Reflection
๋ฆฌํ๋ ์
์ ์์์ Class<T>
Class<T> ์ ๊ทผ ๋ฐฉ๋ฒ
๋ชจ๋ ํด๋์ค๋ฅผ ๋ก๋ฉ ํ ๋ค์ Class<T> ์ธ์คํด์ค ์์ฑ
ํ์
.class
๋ก ์ ๊ทผ ๊ฐ๋ฅ
๋ชจ๋ ์ธ์คํด์ค๋ getClass() ๋ฉ์๋ ๋ณด์
์ธ์คํด์ค.getClass()
๋ก ์ ๊ทผ ๊ฐ๋ฅ
ํด๋์ค๋ฅผ ๋ฌธ์์ด๋ก ์ฝ์ด์ค๋ ๋ฐฉ๋ฒ
ํด๋์คํจ์ค์ ํด๋น ํด๋์ค๊ฐ ์๋ค๋ฉด ClassNotFoundException ๋ฐ์
Copy Class<Book> bookClass = Book.class;
Book book = new Book();
Class<? extends Book> aClass = book.getClass();
Class<?> aClass1 = Class.forName("com.example.java. reflection.Book");
Class<T> ๋ฅผ ํตํด ํ ์ ์๋ ๊ฒ
ํ๋(๋ชฉ๋ก) ๊ฐ์ ธ์ค๊ธฐ
๋ฉ์๋(๋ชฉ๋ก) ๊ฐ์ ธ์ค๊ธฐ
์์ ํด๋์ค ๊ฐ์ ธ์ค๊ธฐ
์ธํฐํ์ด์ค(๋ชฉ๋ก) ๊ฐ์ ธ์ค๊ธฐ
์ ๋
ธํ
์ด์
๊ฐ์ ธ์ค๊ธฐ
์์ฑ์ ๊ฐ์ ธ์ค๊ธฐ ...
.
Annotation & Reflection
Annotaion
@Retention: ํด๋น ์ ๋
ธํ
์ด์
์ ์ธ์ ๊น์ง ์ ์งํ ๊ฒ์ธ๊ฐ. (SOURCE, CLASS, RUNTIME)
@Target: ์ด๋์ ์ฌ์ฉํ ์ ์๋๊ฐ. (TYPE, FIELD, METHOD, PARAMETER ..)
@Inherit: ํด๋น ์ ๋
ธํ
์ด์
์ ํ์ ํด๋์ค๊น์ง ์ ๋ฌํ ๊ฒ์ธ๊ฐ.
Copy @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Inherited
public @interface MyAnnotation {
String name() default "aaron";
int number();
}
Reflection
getAnnotations(): ์์๋ฐ์(@Inherit) ์ ๋
ธํ
์ด์
๊น์ง ์กฐํ
getDeclaredAnnotations(): ์๊ธฐ ์์ ์๋ง ๋ถ์ด์๋ ์ ๋
ธํ
์ด์
์กฐํ
Copy // ์์๋ฐ์(@Inherit) ์ ๋
ธํ
์ด์
๊น์ง ์กฐํ
Arrays.stream(Book.class.getAnnotations()).forEach(System.out::println);
// ์๊ธฐ ์์ ์๋ง ๋ถ์ด์๋ ์ ๋
ธํ
์ด์
์กฐํ
Arrays.stream(MyBook.class.getDeclaredAnnotations()).forEach(System.out::println);
// ์ ๋
ธํ
์ด์
์ ๋ณด ์กฐํ
@Test
void getAnnotatedFieldValue() throws Exception {
Arrays.stream(Book.class.getDeclaredFields()).forEach(f -> {
Arrays.stream(f.getAnnotations()).forEach(a -> {
MyAnotherAnnotation myAnotherAnnotation = (MyAnotherAnnotation) a;
System.out.println(f);
System.out.println(myAnotherAnnotation.value());
});
});
}
.
Edit class information or Run
BallTest
Copy /**
* Class ์ธ์คํด์ค ์์ฑํ๊ธฐ
*/
Class<Ball> ballClass = Ball.class;
// ballClass.newInstance(); -> deprecated
Constructor<Ball> constructor = ballClass.getConstructor(String.class);
// ์์ฑ์๋ก ์ธ์คํด์ค ์์ฑ
Ball ball = constructor.newInstance("myBall");
System.out.println(ball);
/**
* Class Field ์์ ํ๊ธฐ
*/
Field field = Ball.class.getDeclaredField("A");
// public static ์ ํน์ ์ธ์คํด์ค์ ํด๋นํ๋ ๊ฐ์ด ์๋๋ผ์ ํด๋์ค ๊ฐ์ ํด๋นํ๋ฏ๋ก ์ธ์คํด์ค๋ฅผ ์ ๋ฌํ ํ์๊ฐ ์์
System.out.println(field.get(null));
field.set(null, "newA");
System.out.println(field.get(null));
/**
* Instance Field ์์ ํ๊ธฐ
*/
Field field = Ball.class.getDeclaredField("b");
field.setAccessible(true); // private ํ๋ ์ ๊ทผ์ ์ํ ์ค์
// ํน์ ์ธ์คํด์ค๊ฐ ๊ฐ์ง๊ณ ์๋ ๊ฐ์ ๊ฐ์ ธ์์ผ ํ๋ฏ๋ก ์ธ์คํด์ค ํ์
System.out.println(field.get(ball));
field.set(ball, "newB");
System.out.println(field.get(ball));
/**
* Private Method ์คํ
*/
Method method = Ball.class.getDeclaredMethod("c");
method.setAccessible(true);
method.invoke(ball);
/**
* Public Method ์คํ
*/
Method method = Ball.class.getDeclaredMethod("sum", int.class, int.class);
int invoke = (int) method.invoke(ball, 1, 2);
System.out.println(invoke);
.
Reflection ์ ํ์ฉํ์ฌ ๊ฐ๋จํ DI ํ๋ ์์ํฌ ๋ง๋ค์ด๋ณด๊ธฐ
@Inject ์ ์ธ์ผ๋ก ํ๋ ์ฃผ์
์ ํด์ฃผ๋ ์ปจํ
์ด๋ ์๋น์ค
ContainerService.getObject
classType ์ ํด๋นํ๋ ํ์
์ ๊ฐ์ฒด ์์ฑ
ํด๋น ๊ฐ์ฒด์ ํ๋ ์ค์ @Inject ๊ฐ ์๋ค๋ฉด ํด๋น ํ๋๋ ๊ฐ์ด ๋ง๋ค์ด ์ ๊ณต
Copy public static <T> T getObject(Class<T> classType) {
T instance = createInstance(classType);
Arrays.stream(classType.getDeclaredFields()).forEach(f -> {
// @Inject ์ ์ธ ํ๋ ํ์
if (f.getAnnotation(Inject.class) != null) {
Object fieldInstance = createInstance(f.getType());
f.setAccessible(true);
try {
// @Inject ์ ์ธ ํ๋์ ์ธ์คํด์ค ์ฃผ์
f.set(instance, fieldInstance);
} catch (IllegalAccessException e) {
throw new RuntimeException();
}
}
});
return instance;
}
.
Reflection ์ ๋ฆฌ
๋ฆฌํ๋ ์
์ฌ์ฉ ์ ์ฃผ์
์ง๋์น ์ฌ์ฉ(๋ฌด๋ถ๋ณํ ์ธ์คํด์ค ์์ฑ์ผ๋ก)์ ์ฑ๋ฅ ์ด์๋ฅผ ์ผ๊ธฐํ ์ ์์ผ๋ฏ๋ก ๋ฐ๋์ ํ์ํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉ ๊ถ์ฅ
์ปดํ์ผ ํ์์ ํ์ธ๋์ง ์๊ณ ๋ฐํ์ ์์๋ง ๋ฐ์ํ๋ ๋ฌธ์ ๋ฅผ ๋ง๋ค ๊ฐ๋ฅ์ฑ ์กด์ฌ
๋ฆฌํ๋ ์
์ฌ์ฉ ์ฌ๋ก
Spring.
MVC View ์์ ๋์ด์จ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด์ ๋ฐ์ธ๋ฉ ํ ๋
Hibernate.
@Entity ํด๋์ค์ Setter ๊ฐ ์๋ค๋ฉด ๋ฆฌํ๋ ์
์ฌ์ฉ
Reference.
Dynamic Proxy
๋ฐํ์์ ์ธํฐํ์ด์ค/ํด๋์ค์ ํ๋ก์ ์ธ์คํด์ค/ํด๋์ค๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํ๋ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ
Spring Data JPA ๋ ์ด๋ป๊ฒ ๋์ํ ๊น?
์ธํฐํ์ด์ค ํ์
์ ์ธ์คํด์ค๋ ๋๊ฐ ๋ง๋ค์ด ์ค๊น?
JpaRepository ์ธํฐํ์ด์ค๋ฅผ ์์๋ฐ์ผ๋ฉด ๊ฐ์ฒด๋ ์์ฑ๋๊ณ , ๋น์ผ๋ฅด๋ ๋ฑ๋ก
Spring AOP ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๋ฉฐ RepositoryFactorySupport ์์ ํ๋ก์ ๊ฐ์ฒด ์์ฑ
์์ฑ๋ ํ๋ก์ ๊ฐ์ฒด๊ฐ ๋น์ผ๋ก ๋ฑ๋ก๋๊ณ ์ฃผ์
Dynamic Proxy ์ฌ์ฉ ์
Hibernate lazy initialzation ...
.
Proxy Pattern
ํ๋ก์์ ๋ฆฌ์ผ ์๋ธ์ ํธ๊ฐ ๊ณต์ ํ๋ ์ธํฐํ์ด์ค๊ฐ ์๊ณ , ํด๋ผ์ด์ธํธ๋ ํด๋น ์ธํฐํ์ด์ค ํ์
์ผ๋ก ํ๋ก์ ์ฌ์ฉ
ํด๋ผ์ด์ธํธ๋ ํ๋ก์๋ฅผ ๊ฑฐ์ณ์ ๋ฆฌ์ผ ์๋ธ์ ํธ๋ฅผ ์ฌ์ฉ
ํ๋ก์๋ ๋ฆฌ์ผ ์๋ธ์ ํธ์ ๋ํ ์ ๊ทผ ๊ด๋ฆฌ, ๋ถ๊ฐ ๊ธฐ๋ฅ ์ ๊ณต, ๋ฆฌํด๊ฐ ๋ณ๊ฒฝ ๊ฐ๋ฅ
๋ฆฌ์ผ ์๋ธ์ ํธ๋ ์์ ์ด ํด์ผ ํ ์ผ๋ง ํ๋ฉด์(SRP ) ํ๋ก์ ์ฌ์ฉ
๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ(์ ๊ทผ ์ ํ, ๋ก๊น
, ํธ๋์ญ์
๋ฑ) ์ ๊ณต ์ ํ๋ก์ ํจํด ์ ์ฃผ๋ก ์ฌ์ฉ
๋จ์
ํ๋ก์ ํจํด์ผ๋ก ๊ตฌํํ๋ ๊ฒ์ ๋ฒ๊ฑฐ๋ก์ด ์ผ
๋ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ๋๋ง๋ค ๋ณ๋ ํ๋ก์ ์์ฑ ํ์
ํ๋ก์๋ก ํ๋ก์๋ฅผ ๊ฐ์ธ์ผ ํ๋ ์ผ๋ ๋ฐ์
๋ชจ๋ ๊ตฌํ์ฒด์์ ์๋ ํ๊ฒ์ผ๋ก ์์ํ๋ฉด์ ์ค๋ณต ์ฝ๋ ๋ฐ์
Proxy Pattern example
ํ๋ก์ ํจํด์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ๋์ ์ผ๋ก ๋ฐํ์์ ํ๋ก์๋ฅผ ์์ฑํด๋ด๋ ๋ค์ด๋๋ฏน ํ๋ก์
๋ฑ์ฅ
.
Dynamic Proxy
๋ฐํ์์ ํน์ ์ธํฐํ์ด์ค๋ค์ ๊ตฌํํ๋ ํด๋์ค ๋๋ ์ธ์คํด์ค๋ฅผ ๋ง๋๋ ๊ธฐ์
Copy BookService defaultBookService = (BookService) Proxy.newProxyInstance(
BookService.class.getClassLoader(),
new Class[]{BookService.class}, // ์ด๋ค ์ธํ
ํ์ด์ค ํ์
์ ๊ตฌํ์ฒด์ธ์ง
new InvocationHandler() { // ํ๋ก์์ ์ด๋ค ๋ฉ์๋๊ฐ ํธ์ถ์ด ๋ ๋ ๊ทธ ๋ฉ์๋ ํธ์ถ์ ์ด๋ป๊ฒ ์ฒ๋ฆฌํ ์ง์ ๋ํ ์ค๋ช
BookService bookService = new DefaultBookService();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("rent")) {
System.out.println("qqqqq");
Object invoke = method.invoke(bookService, args);
System.out.println("zzzzz");
return invoke;
}
return method.invoke(bookService, args);
}
}
);
๋จ์ .
ํด๋์ค ๊ธฐ๋ฐ์ ํ๋ก์ ์์ฑ ๋ถ๊ฐ -> ์ธํฐํ์ด์ค๊ฐ ์์ ๊ฒฝ์ฐ ๋ค์ด๋๋ฏน ํ๋ก์ ์ ์ฉ ๋ถ๊ฐ
๋ถ๊ฐ๊ธฐ๋ฅ์ด ๋ง์์ง์๋ก ์ฝ๋๊ฐ ์ปค์ง๋ ์ ์ฐํ์ง ์์ ๊ตฌ์กฐ
ํ๋ก์ ๊ธฐ๋ฐ AOP ์ธ ์คํ๋ง AOP ๋ฑ์ฅ
.
Class Proxy
์ธํฐํ์ด์ค ์์ด ํ๋ก์ ๋ง๋ค๊ธฐ
CGlib
Spring, Hibernate ์์๋ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋ฒ์ ํธํ์ฑ์ด ์ข์ง ์์์ ์๋ก ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ด๋ถ์ ๋ด์ฅ๋ ํํ๋ก ์ ๊ณต๋๊ธฐ๋ ํจ
Copy MethodInterceptor handler = new MethodInterceptor() {
BallService bookService = new BallService();
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (method.getName().equals("rent")) {
System.out.println("bbbbb");
Object invoke = method.invoke(bookService, args);
System.out.println("ccccc");
return invoke;
}
return method.invoke(bookService, args);
}
};
BallService ballService = (BallService) Enhancer.create(BallService.class, handler);
Book book = new Book();
book.setTitle("spring");
ballService.rent(book);
ByteBuddy
๋ฐ์ดํธ ์ฝ๋ ์กฐ์ ๋ฟ ์๋๋ผ ๋ฐํ์(๋ค์ด๋๋ฏน) ํ๋ก์ ์์ฑ ์์๋ ์ฌ์ฉ
Copy Class<? extends BallService> proxyClass = new ByteBuddy().subclass(BallService.class)
.method(named("rent")).intercept(InvocationHandlerAdapter.of(new InvocationHandler() {
BallService bookService = new BallService();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("qqqq");
Object invoke = method.invoke(bookService, args);
System.out.println("eeee");
return invoke;
}
}))
.make().load(BallService.class.getClassLoader()).getLoaded();
BallService ballService = proxyClass.getConstructor(null).newInstance();
Book book = new Book();
book.setTitle("spring");
ballService.rent(book);
์ธํฐํ์ด์ค ์์ด ํ๋ก์๋ฅผ ์์ฑํ ๊ฒฝ์ฐ ๋จ์
์์์ ์ฌ์ฉํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ ํ๋ก์ ์์ฑ ๋ถ๊ฐ
Final ํด๋์ค์ธ ๊ฒฝ์ฐ
Private ์์ฑ์๋ง ์๋ ๊ฒฝ์ฐ
๊ฐ๊ธ์ ํ๋ก์ ์ ์ฉ ์ ์ธํฐํ์ด์ค๋ฅผ ๋ง๋ค์ด์ ์ธํฐํ์ด์ค ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅ
Annotation processor
์ ๋
ธํ
์ด์
ํ๋ก์ธ์ ์ฌ์ฉ ์
Lombok
ํ์ค์ผ๋ก ์์ฑํด์ผ ํ๋ ์ฝ๋๋ฅผ ๊ฐ๋ฐ์ ๋์ ์์ฑํด ์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
AutoService
java.util.ServiceLoader ์ฉ ํ์ผ ์์ฑ ์ ํธ๋ฆฌํฐ
@Override
Reflection API ๋ฅผ ์ฌ์ฉํ์ฌ ์ํผ ํด๋์ค์ ํด๋นํ๋ ๋ฉ์๋์ ์ผ์นํ๋ ํญ๋ชฉ์ด ์๋ค๋ฉด ์ปดํ์ผ ์ค๋ฅ ๋ฐ์
Dagger
์ปดํ์ผ ํ์ DI ์ ๊ณต
์ฅ์
๋ฐํ์ ๋น์ฉ์ด ์ ๋ก
๋จ์
๊ธฐ์กด ํด๋์ค ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ ๋๋ ์ฝ๊ฐ์ hack ํ์
.
Lombok
@Getter, @Setter ๋ฑ์ ์ ๋
ธํ
์ด์
๊ณผ ์ ๋
ธํ
์ด์
ํ๋ก์ธ์๋ฅผ ์ ๊ณตํ์ฌ ํ์ค ์์ฑ ์ฝ๋๋ฅผ ๊ฐ๋ฐ์ ๋์ ์์ฑํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๋กฌ๋ณต์ ๋์ ์๋ฆฌ
๋กฌ๋ณต์ ๋
ผ๋ ๊ฑฐ๋ฆฌ
๊ณต๊ฐ๋ API ๊ฐ ์๋ ์ปดํ์ผ๋ฌ ๋ด๋ถ ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ์์ค ์ฝ๋๋ฅผ ์กฐ์
ํนํ ์ดํด๋ฆฝ์ค์ ๊ฒฝ์ฐ์ java agent ๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํ์ผ๋ฌ ํด๋์ค๊น์ง ์กฐ์ํ์ฌ ์ฌ์ฉ
ํด๋น ํด๋์ค๋ค ์ญ์ ๊ณต๊ฐ๋ API ๊ฐ ์๋๋ค๋ณด๋ ๋ฒ์ ํธํ์ฑ์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์์
๊ทธ๋ผ์๋ ์์ฒญ๋ ํธ๋ฆฌํจ์ผ๋ก ๋๋ฆฌ ์ฐ์ด๊ณ , ๋์์ด ๋ช๊ฐ์ง ์์ง๋ง ๋กฌ๋ณต์ ๋ชจ๋ ๊ธฐ๋ฅ๊ณผ ํธ์์ฑ์ ๋์ฒด ๋ถ๊ฐ
.
Annotation processor
Processor Interface
์ฌ๋ฌ ๋ผ์ด๋(rounds)์ ๊ฑฐ์ณ ์์ค ๋ฐ ์ปดํ์ผ ๋ ์ฝ๋๋ฅผ ์ฒ๋ฆฌ
AutoService
์ปดํ์ผ ์์ ์ ์ ๋
ธํ
์ด์
ํ๋ก์ธ์๋ฅผ ์ฌ์ฉํ์ฌ META-INF/services/javax.annotation.processor.Processor
ํ์ผ ์๋ ์์ฑ
javapoet
์์ค ์ฝ๋ ์์ฑ ์ ํธ๋ฆฌํฐ
Interface Filer
์์ค ์ฝ๋, ํด๋์ค ์ฝ๋ ๋ฐ ๋ฆฌ์์ค๋ฅผ ์์ฑํ ์ ์๋ ์ธํฐํ์ด์ค
Copy @AutoService(Processor.class)
public class MagicMojaProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of(Magic.class.getName());
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Magic.class);
for (Element element : elements) {
Name elementName = element.getSimpleName();
// Magic annotation ์ด Interface ๊ฐ ์๋ ๋ค๋ฅธ ๊ณณ์ ์ ์ธ๋์ด ์์ ๊ฒฝ์ฐ.
if (element.getKind() != ElementKind.INTERFACE) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Magic annotation can not be used on" + elementName);
} else {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Processing " + elementName);
}
TypeElement typeElement = (TypeElement) element;
ClassName className = ClassName.get(typeElement);
// ๋ฉ์๋ ์์ฑ
MethodSpec pullOut = MethodSpec.methodBuilder("pullOut")
.addModifiers(Modifier.PUBLIC)
.returns(String.class)
.addStatement("return $S", "Rabbit!")
.build();
// ํด๋์ค ์์ฑ
TypeSpec magicMoja = TypeSpec.classBuilder("MagicMoja")
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(className)
.addMethod(pullOut)
.build();
Filer filer = processingEnv.getFiler();
try {
JavaFile.builder(className.packageName(), magicMoja)
.build()
.writeTo(filer);
} catch (IOException e) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "FATALERROR:" + e);
}
}
return true;
}
}
์ฐธ๊ณ .