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: ๋Ÿฐํƒ€์ž„ ์ค‘ ๋™์ ์œผ๋กœ ๋ถ™์ด๋Š” ๋ฐฉ์‹

      • java.lang.instrument ์‚ฌ์šฉ

    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