The Java

λ°±κΈ°μ„ λ‹˜μ˜ 더 μžλ°”, Java 8, 더 μžλ°”, μ½”λ“œλ₯Ό μ‘°μž‘ν•˜λŠ” λ‹€μ–‘ν•œ 방법 κ°•μ˜λ₯Ό μš”μ•½ν•œ λ‚΄μš©μž…λ‹ˆλ‹€.

Java 8

Java 8

.

LTS(Long-Term-Support)

  • λΉ„-LTS

    • 배포 μ£ΌκΈ°: 6κ°œμ›”

    • 지원 κΈ°κ°„: 배포 이후 6κ°œμ›”(λ‹€μŒ 버전이 λ‚˜μ˜€λ©΄ 지원 μ’…λ£Œ)

  • LTS

    • 배포 μ£ΌκΈ°: 3λ…„(맀 6번째 배포판이 LTS)

    • 지원 κΈ°κ°„: 5년이상(JDK 제곡 밴더와 μ΄μš©ν•˜λŠ” μ„œλΉ„μŠ€μ— 따라 차이)

    • μ‹€μ œ μ„œλΉ„μŠ€ 운영 ν™˜κ²½μ—μ„œλŠ” LTS 버전 ꢌμž₯

  • 맀년 3μ›”κ³Ό 9월에 μƒˆ 버전 배포

Functional Interface & Lambda

ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(Functional Interface)

@FunctionalInterface
public interface RunSomething {
    void doIt();
}
  • 좔상 λ©”μ†Œλ“œλ₯Ό λ”± ν•˜λ‚˜λ§Œ 가지고 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€

  • SAM(Single Abstract Method) μΈν„°νŽ˜μ΄μŠ€

  • @FuncationInterface μ• λ…Έν…Œμ΄μ…˜μ„ 가지고 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€

.

λžŒλ‹€ ν‘œν˜„μ‹(Lambda Expressions)

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 νƒ€μž…μ„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

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 νƒ€μž…μ„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

  • R apply(T t, U u)

@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 νƒ€μž…μ„ λ°›μ•„μ„œ 아무값도 λ¦¬ν„΄ν•˜μ§€ μ•ŠλŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

@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 νƒ€μž…μ˜ 값을 μ œκ³΅ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

@Test
void get() throws Exception {
    /**
     * T get()
     */
    Supplier<Integer> get10 = () -> 10;
    Assertions.assertEquals(10, get10.get());
}

Predicate<T>

  • T νƒ€μž…μ„ λ°›μ•„μ„œ boolean을 λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

  • ν•¨μˆ˜ μ‘°ν•©μš© λ©”μ†Œλ“œ

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 상속)

  • μž…λ ₯κ°’ ν•˜λ‚˜λ₯Ό λ°›μ•„μ„œ λ™μΌν•œ νƒ€μž…μ„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

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>의 νŠΉμˆ˜ν•œ ν˜•νƒœ

  • λ™μΌν•œ νƒ€μž…μ˜ μž…λ ΅κ°’ 두 개λ₯Ό λ°›μ•„ λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜ μΈν„°νŽ˜μ΄μŠ€

@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 λ₯Ό μ‚¬μš©ν•΄μ„œ λ©”μ†Œλ“œ, μƒμ„±μž 호좜λ₯Ό 맀우 κ°„κ²°ν•˜κ²Œ ν‘œν˜„ κ°€λŠ₯

@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

/**
 * 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 κΈ°λ³Έ λ©”μ†Œλ“œ

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()

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 κΈ°λ³Έ λ©”μ†Œλ“œ 및 μŠ€νƒœν‹± λ©”μ†Œλ“œ

  • thenComparing()

  • static reverseOrder() / naturalOrder()

  • static nullsFirst() / nullsLast()

  • static comparing()

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)

  • getExactSizeIfKnown()

  • hasCharacteristics()

  • getComparator()

Stream

Package java.util.stream

Stream

  • 데이터λ₯Ό λ‹΄κ³  μžˆλŠ” μ €μž₯μ†Œ(μ»¬λ ‰μ…˜)κ°€ μ•„λ‹ˆλΌ, μ–΄λ– ν•œ μ—°μ†λœ 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” μ˜€νΌλ ˆμ΄μ…˜λ“€μ˜ λͺ¨μŒ

  • 슀트림 처리 μ‹œ 데이터 원본은 λ³€κ²½ν•˜μ§€ μ•ŠμŒ

  • 슀트림으둜 μ²˜λ¦¬ν•˜λŠ” λ°μ΄ν„°λŠ” 였직 ν•œ 번만 처리

  • μ‹€μ‹œκ°„μœΌλ‘œ 슀트림 데이터가 λ“€μ–΄μ˜¬ 경우 λ¬΄ν•œ 처리(Short Circuit λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•΄μ„œ μ œν•œ κ°€λŠ₯)

  • μ€‘κ°œ μ˜€νΌλ ˆμ΄μ…˜μ€ 근본적으둜 lazy νŠΉμ„±μ„ 가짐

  • 데이터가 λ°©λŒ€ν•œ 경우 parallelStream() 으둜 μ†μ‰½κ²Œ 병렬 처리 κ°€λŠ₯

    • μŠ€λ ˆλ“œ 생성, λ³‘λ ¬μ²˜λ¦¬ ν›„ μˆ˜μ§‘, μŠ€λ ˆλ“œ κ°„ μ»¨ν…μŠ€νŠΈ μŠ€μœ„μΉ­ λ“±μ˜ λΉ„μš©μœΌλ‘œ 무쑰건 λΉ¨λΌμ§€λŠ” 건 μ•„λ‹˜

슀트림 νŒŒμ΄ν”„λΌμΈ

  • 0 λ˜λŠ” λ‹€μˆ˜μ˜ μ€‘κ°œ μ˜€νΌλ ˆμ΄μ…˜κ³Ό ν•œ 개의 μ’…λ£Œ μ˜€νΌλ ˆμ΄μ…˜μœΌλ‘œ ꡬ성

  • 슀트림의 데이터 μ†ŒμŠ€λŠ” 였직 터미널 μ˜€νΌλ„€μ΄μ…˜μ„ μ‹€ν–‰ν•  λ•Œμ—λ§Œ 처리

μ€‘κ°œ μ˜€νΌλ ˆμ΄μ…˜(intermediate operation)

  • Stream 리턴

  • Stateless / Stateful μ˜€νΌλ ˆμ΄μ…˜μœΌλ‘œ 더 μƒμ„Έν•˜κ²Œ ꡬ뢄 κ°€λŠ₯

    • λŒ€λΆ€λΆ„ Stateless operation

    • 이전 μ†ŒμŠ€ 데이터λ₯Ό μ°Έμ‘°ν•΄μ•Ό ν•˜λŠ” μ˜€νΌλ ˆμ΄μ…˜(ex. distinct, sorted)은 Stateful μ˜€νΌλ ˆμ΄μ…˜

  • filter, map, limit, skip, sorted ...

μ’…λ£Œ μ˜€νΌλ ˆμ΄μ…˜(terminal operation)

  • Stream 리턴 X

  • collect, allMatch, count, forEach, min, max ...

.

Stream API

StreamTest

필터링

@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());
}

슀트림 λ³€κ²½

@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());
}

슀트림 생성과 μ œν•œ

@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);
}

μŠ€νŠΈλ¦Όμ— μžˆλŠ” 데이터가 νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ”μ§€ 확인

@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

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 생성

Optional.of()
Optional.ofNullable()
Optional.empty()

Optional κ°’ 포함 μ—¬λΆ€ 확인

optional.isPresent()
optional.isEmpty() // Java 11 이후

Optional κ°’ κ°€μ Έμ˜€κΈ°

optional.get(); // λΉ„μ–΄μžˆμ„ 경우 NoSuchElementException μ˜ˆμ™Έ λ°œμƒ

Optional 에 값이 μ‘΄μž¬ν•  경우 λ™μž‘ μˆ˜ν–‰

optional.ifPresent(oc -> System.out.println(oc.getTitle()));

Optional 에 값이 μžˆμ„ 경우 κΊΌλ‚΄κ³ , 무쑰건 μƒˆλ‘œμš΄ 클래슀 생성

optional.orElseGet(OptionalTest::createNewClass);

Optional 에 값이 μžˆμ„ 경우 κΊΌλ‚΄κ³ , μ—†μœΌλ©΄ μƒˆλ‘œμš΄ 클래슀 제곡

result.orElseGet(OptionalTest::createNewClass);

Optional 에 값이 μžˆμ„ 경우 κΊΌλ‚΄κ³ , μ—†μœΌλ©΄ μ˜ˆμ™Έ

assertThrows(NoSuchElementException.class, () -> {
    result.orElseThrow();
});

assertThrows(IllegalStateException.class, () -> {
    result.orElseThrow(IllegalStateException::new);
});

Optional 값을 필터링

Optional<OnlineClass> jpaClass = result.filter(Predicate.not(OnlineClass::isClosed));

Optional 값을 맀핑(λ³€ν™˜)

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

  • JSR-310 슀팩 κ΅¬ν˜„μ²΄ 제곡

  • 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)

  • 보톡 μ‹œκ°„μ„ μž¬λŠ” 경우 μ‚¬μš©

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 의 νŠΉμ • μΌμ‹œ

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]

λ‚ μ§œ μ—°μ‚°

LocalDateTime now = LocalDateTime.now();
LocalDateTime plusDay = now.plus(10, ChronoUnit.DAYS);
LocalDateTime plusMonth = now.plus(2, ChronoUnit.MONTHS);

κΈ°κ°„ ν‘œν˜„

// 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

// 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 둜 μƒν˜Έ λ³€ν™˜ κ°€λŠ₯

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

  • Thread / Runnable

μ“°λ ˆλ“œ μ£Όμš” κΈ°λŠ₯(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

/**
 * 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

/**
 * 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

.

λΉ„λ™κΈ°λ‘œ μž‘μ—… μ‹€ν–‰ν•˜κΈ°

/**
 * 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());

.

콜백 μ œκ³΅ν•˜κΈ°

/**
 * 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();

.

μ‘°ν•©ν•˜κΈ°

/**
 * 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();
}

.

μ˜ˆμ™Έμ²˜λ¦¬

/**
 * 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 포함 λͺ¨λ“  νƒ€μž… 선언뢀에 μ‚¬μš© κ°€λŠ₯

    static class XXX<@Chicken T> {
      /**
       * <C> : type parameter
        * C : type
        */
      public static <@Chicken C> void print(C c){
    
      }
    }
  • μ• λ…Έν…Œμ΄μ…˜ 쀑볡 μ‚¬μš© κ°€λŠ₯

    // 쀑볡 μ‚¬μš©ν•  μ• λ…Έν…Œμ΄μ…˜
    @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 ν”„λ ˆμž„μ›Œν¬λ₯Ό μ‚¬μš©ν•΄μ„œ 배열을 λ³‘λ ¬λ‘œ μ •λ ¬ν•˜λŠ” κΈ°λŠ₯ 제곡

  • 병렬 μ •λ ¬ μ•Œκ³ λ¦¬λ“¬

    • 배열을 λ‘˜λ‘œ 계속 μͺΌκ°  ν›„ ν•©μΉ˜λ©΄μ„œ μ •λ ¬

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 μ˜μ—­)

  • κΈ°λ³Έκ°’μœΌλ‘œ μ œν•œλœ 크기λ₯Ό 가지고 있음

  • PermGen Elimination project is promoting

    -XX:PermSize=N // PermGen 초기 μ‚¬μ΄μ¦ˆ μ„€μ •
    -XX:MaxPermSize=N // PermGen μ΅œλŒ€ μ‚¬μ΄μ¦ˆ μ„€μ •

Metaspace

  • 클래슀 메타데이터λ₯Ό λ‹΄λŠ” μ €μž₯μ†Œ(Heap μ˜μ—­μ΄ μ•„λ‹ˆλΌ, Native Memory μ˜μ—­)

  • κΈ°λ³Έκ°’μœΌλ‘œ μ œν•œλœ 크기λ₯Ό 가지고 μžˆμ§€ μ•ŠμŒ(ν•„μš”ν•œ 만큼 계속 증가)

  • java 8 λΆ€ν„°λŠ” PermGen κ΄€λ ¨ μ˜΅μ…˜μ€ λ¬΄μ‹œ

    -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++둜 μž‘μ„±λœ 라이브러리

  • JNI λ₯Ό 톡해 μ‚¬μš©

μ°Έκ³ 

.

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 파일 자체λ₯Ό λ³€κ²½μ‹œν‚€λŠ” 방법

    • ꢌμž₯ν•˜λŠ” 라이브러리

    new ByteBuddy().redefine(Moja.class)
            .method(named("pullOut")).intercept(FixedValue.value("Rabbit!"))
            .make().saveIn(new File("../target/classes/"))
  • Javassist

    • 클래슀 λ‘œλ”κ°€ 클래슀λ₯Ό μ½μ–΄μ˜¬ λ•Œ javaagent λ₯Ό κ±°μ³μ„œ λ³€κ²½λœ λ°”μ΄νŠΈμ½”λ“œλ₯Ό μ½μ–΄μ˜΄

      • premain: μ‹œμž‘ μ‹œ λΆ™μ΄λŠ” 방식

      • agentmain: λŸ°νƒ€μž„ 쀑 λ™μ μœΌλ‘œ λΆ™μ΄λŠ” 방식

    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);
              }
  • CGlib

Reflection

λ¦¬ν”Œλ ‰μ…˜μ˜ μ‹œμž‘μ€ Class<T>

Class<T> μ ‘κ·Ό 방법

  • λͺ¨λ“  클래슀λ₯Ό λ‘œλ”© ν•œ λ‹€μŒ Class<T> μΈμŠ€ν„΄μŠ€ 생성

    • νƒ€μž….class 둜 μ ‘κ·Ό κ°€λŠ₯

  • λͺ¨λ“  μΈμŠ€ν„΄μŠ€λŠ” getClass() λ©”μ†Œλ“œ 보유

    • μΈμŠ€ν„΄μŠ€.getClass() 둜 μ ‘κ·Ό κ°€λŠ₯

  • 클래슀λ₯Ό λ¬Έμžμ—΄λ‘œ μ½μ–΄μ˜€λŠ” 방법

    • Class.forName("FQCN")

    • ν΄λž˜μŠ€νŒ¨μŠ€μ— ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ μ—†λ‹€λ©΄ ClassNotFoundException λ°œμƒ

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: ν•΄λ‹Ή μ• λ…Έν…Œμ΄μ…˜μ„ ν•˜μœ„ ν΄λž˜μŠ€κΉŒμ§€ 전달할 것인가.

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Inherited
public @interface MyAnnotation {
    String name() default "aaron";
    int number();
}

Reflection

  • getAnnotations(): 상속받은(@Inherit) μ• λ…Έν…Œμ΄μ…˜κΉŒμ§€ 쑰회

  • getDeclaredAnnotations(): 자기 μžμ‹ μ—λ§Œ λΆ™μ–΄μžˆλŠ” μ• λ…Έν…Œμ΄μ…˜ 쑰회

// 상속받은(@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

/**
 * 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 κ°€ μžˆλ‹€λ©΄ ν•΄λ‹Ή ν•„λ“œλ„ 같이 λ§Œλ“€μ–΄ 제곡

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 μ‚¬μš© 예

  • Spring Data JPA

  • Spring AOP

  • Mockito

  • Hibernate lazy initialzation ...

.

Proxy Pattern

  • ν”„λ‘μ‹œμ™€ 리얼 μ„œλΈŒμ νŠΈκ°€ κ³΅μœ ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€κ°€ 있고, ν΄λΌμ΄μ–ΈνŠΈλŠ” ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μœΌλ‘œ ν”„λ‘μ‹œ μ‚¬μš©

  • ν΄λΌμ΄μ–ΈνŠΈλŠ” ν”„λ‘μ‹œλ₯Ό κ±°μ³μ„œ 리얼 μ„œλΈŒμ νŠΈλ₯Ό μ‚¬μš©

    • ν”„λ‘μ‹œλŠ” 리얼 μ„œλΈŒμ νŠΈμ— λŒ€ν•œ μ ‘κ·Ό 관리, λΆ€κ°€ κΈ°λŠ₯ 제곡, 리턴값 λ³€κ²½ κ°€λŠ₯

  • 리얼 μ„œλΈŒμ  νŠΈλŠ” μžμ‹ μ΄ ν•΄μ•Ό ν•  일만 ν•˜λ©΄μ„œ(SRP) ν”„λ‘μ‹œ μ‚¬μš©

    • 뢀가적인 κΈ°λŠ₯(μ ‘κ·Ό μ œν•œ, λ‘œκΉ…, νŠΈλžœμž­μ…˜ λ“±) 제곡 μ‹œ ν”„λ‘μ‹œ νŒ¨ν„΄μ„ 주둜 μ‚¬μš©

단점

  • ν”„λ‘μ‹œ νŒ¨ν„΄μœΌλ‘œ κ΅¬ν˜„ν•˜λŠ” 것은 번거둜운 일

  • 뢀가적인 κΈ°λŠ₯을 μΆ”κ°€ν•  λ•Œλ§ˆλ‹€ 별도 ν”„λ‘μ‹œ 생성 ν•„μš”

  • ν”„λ‘μ‹œλ‘œ ν”„λ‘μ‹œλ₯Ό 감싸야 ν•˜λŠ” 일도 λ°œμƒ

  • λͺ¨λ“  κ΅¬ν˜„μ²΄μ—μ„œ μ›λž˜ νƒ€κ²ŸμœΌλ‘œ μœ„μž„ν•˜λ©΄μ„œ 쀑볡 μ½”λ“œ λ°œμƒ

Proxy Pattern example

ν”„λ‘μ‹œ νŒ¨ν„΄μ˜ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ™μ μœΌλ‘œ λŸ°νƒ€μž„μ— ν”„λ‘μ‹œλ₯Ό μƒμ„±ν•΄λ‚΄λŠ” λ‹€μ΄λ‚˜λ―Ή ν”„λ‘μ‹œ λ“±μž₯

.

Dynamic Proxy

λŸ°νƒ€μž„μ— νŠΉμ • μΈν„°νŽ˜μ΄μŠ€λ“€μ„ κ΅¬ν˜„ν•˜λŠ” 클래슀 λ˜λŠ” μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” 기술

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 μ—μ„œλ„ μ‚¬μš©ν•˜λŠ” 라이브러리

  • 버전 ν˜Έν™˜μ„±μ΄ 쒋지 μ•Šμ•„μ„œ μ„œλ‘œ λ‹€λ₯Έ 라이브러리 내뢀에 λ‚΄μž₯된 ν˜•νƒœλ‘œ μ œκ³΅λ˜κΈ°λ„ 함

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

  • λ°”μ΄νŠΈ μ½”λ“œ μ‘°μž‘ 뿐 μ•„λ‹ˆλΌ λŸ°νƒ€μž„(λ‹€μ΄λ‚˜λ―Ή) ν”„λ‘μ‹œ 생성 μ‹œμ—λ„ μ‚¬μš©

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

  • ServiceProvider λ ˆμ§€μŠ€νŠΈλ¦¬ 생성기

  • 컴파일 μ‹œμ μ— μ• λ…Έν…Œμ΄μ…˜ ν”„λ‘œμ„Έμ„œλ₯Ό μ‚¬μš©ν•˜μ—¬ META-INF/services/javax.annotation.processor.Processor 파일 μžλ™ 생성

javapoet

  • μ†ŒμŠ€ μ½”λ“œ 생성 μœ ν‹Έλ¦¬ν‹°

Interface Filer

  • μ†ŒμŠ€ μ½”λ“œ, 클래슀 μ½”λ“œ 및 λ¦¬μ†ŒμŠ€λ₯Ό 생성할 수 μžˆλŠ” μΈν„°νŽ˜μ΄μŠ€

@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;
    }
}

μ°Έκ³ .

Last updated