Amenable
Amenable's Blog
Amenable
  • λΆ„λ₯˜ 전체보기 (189)
    • πŸ“‚ JAVA (87)
      • μ΄νŽ™ν‹°λΈŒ μžλ°” (65)
      • μ£Όμš” κ°œλ… (22)
    • πŸ“‚ 개발 μ„œμ  (22)
      • μ‹€μš©μ£Όμ˜ ν”„λ‘œκ·Έλž˜λ¨Έ (1)
      • 객체지ν–₯의 사싀과 μ˜€ν•΄ (2)
      • 클린 μ½”λ“œ (8)
      • ν•¨κ»˜ 자라기 (1)
      • 그림으둜 λ°°μš°λŠ” HTTP&Network Basic (10)
    • πŸ“‚ λ°μ΄ν„°λ² μ΄μŠ€ (8)
      • κ°œλ… (8)
      • λ¬Έμ œν’€μ΄ (0)
    • πŸ“‚ λ„€νŠΈμ›Œν¬ (14)
      • κ°œλ… (6)
      • 성곡과 μ‹€νŒ¨λ₯Ό κ²°μ •ν•˜λŠ” 1%의 λ„€νŠΈμ›Œν¬ 원리 (8)
    • πŸ“‚ μŠ€ν”„λ§ (13)
      • κΈ°λ³Έ κ°œλ… (13)
    • πŸ“‚ WEB (5)
    • πŸ“‚ 자료ꡬ쑰 (12)
      • κ°œλ… (2)
      • μ •λ ¬ (8)
      • 트리 (2)
    • πŸ“‚ μ•Œκ³ λ¦¬μ¦˜ (10)
      • μ΅œμ†Œμ‹ μž₯트리 (2)
      • μ΅œλ‹¨ 경둜 (2)
      • λ¬Έμžμ—΄ (2)
      • ETC (4)
    • πŸ“‚ μ•Œκ³ λ¦¬μ¦˜_λ¬Έμ œν’€μ΄ (4)
      • BOJ_λ°±μ€€ (4)
    • πŸ“‚ ν”„λ‘œκ·Έλž˜λ° (3)
    • πŸ“‚ DevOps (2)
      • 배포 (2)
    • πŸ“‚ ν›„κΈ° (8)
      • μš°μ•„ν•œ ν…Œν¬μ½”μŠ€(ν”„λ¦¬μ½”μŠ€) (4)
      • 2023λ…„ (3)
      • 2024λ…„ (1)
    • πŸ“‚ 회고 (1)
      • 2023λ…„ (1)

λΈ”λ‘œκ·Έ 메뉴

  • πŸš€ GitHub

ν‹°μŠ€ν† λ¦¬

hELLO Β· Designed By μ •μƒμš°.
Amenable

Amenable's Blog

λͺ…λͺ… νŒ¨ν„΄λ³΄λ‹€ μ• λ„ˆν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ39)]
πŸ“‚ JAVA/μ΄νŽ™ν‹°λΈŒ μžλ°”

λͺ…λͺ… νŒ¨ν„΄λ³΄λ‹€ μ• λ„ˆν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ39)]

2023. 7. 6. 23:16

πŸ“™ λͺ…λͺ… νŒ¨ν„΄μ˜ 문제점

  μ „ν†΅μ μœΌλ‘œ λ„κ΅¬λ‚˜ ν”„λ ˆμž„μ›Œν¬κ°€ νŠΉλ³„νžˆ 닀뀄야 ν•  ν”„λ‘œκ·Έλž¨ μš”μ†Œμ—λŠ” λ”± κ΅¬λΆ„λ˜λŠ” λͺ…λͺ… νŒ¨ν„΄μ„ μ μš©ν•΄ μ™”λ‹€.

  예λ₯Ό λ“€μ–΄, ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬μΈ JUnit은 버전 3κΉŒμ§€ ν…ŒμŠ€νŠΈ λ©”μ„œλ“œ 이름을 test둜 μ‹œμž‘ν•˜κ²Œλ” ν–ˆλ‹€. 효과적인 λ°©λ²•μ΄μ§€λ§Œ μ•„λž˜μ™€ 같은 3κ°€μ§€ 큰 단점이 μžˆλ‹€.

  1. μ˜€νƒ€κ°€ λ‚˜λ©΄ μ•ˆ λœλ‹€.
    μ‹€μˆ˜λ‘œ μ΄λ¦„을 tsetSafetyOverride둜 μ§€μœΌλ©΄ μ΄ λ©”μ„œλ“œλŠ” λ¬΄μ‹œλœλ‹€.
  2. μ˜¬λ°”λ₯Έ ν”„λ‘œκ·Έλž¨ μš”μ†Œμ—μ„œλ§Œ μ‚¬μš©λ˜λ¦¬λΌ λ³΄μ¦ν•  λ°©λ²•이 μ—†λ‹€.
    클래슀 μ΄λ¦„을 TestSafetyMechanisms둜 μ§€μ–΄ μ΄ ν΄λž˜μŠ€μ— μ •μ˜λœ ν…ŒμŠ€νŠΈ λ©”μ„œλ“œλ“€μ„ μˆ˜ν–‰ν•˜κΈΈ λ°”λΌμ§€λ§Œ JUnit은 ν΄λž˜μŠ€ μ΄λ¦„μ—λŠ” κ΄€μ‹¬μ΄ μ—†λ‹€.
  3. ν”„λ‘œκ·Έλž¨ μš”μ†Œλ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ μ „달할 λ§ˆλ•…ν•œ λ°©λ²•이 μ—†λ‹€.
    νŠΉμ • μ˜ˆμ™Έλ₯Ό λ˜μ Έμ•Όλ§Œ μ„±κ³΅ν•˜λŠ” ν…ŒμŠ€νŠΈκ°€ μžˆλ‹€κ³  ν•  λ•Œ, κΈ°λŒ€ν•˜λŠ” μ˜ˆμ™Έ νƒ€μž…을 ν…ŒμŠ€νŠΈμ— λ§€κ°œλ³€μˆ˜λ‘œ μ „달할 μˆ˜ μ—†λ‹€.

 

 

πŸ“™ μ˜ˆμ‹œ 1 - λ§ˆμ»€(marker) μ• λ„ˆν…Œμ΄μ…˜

  μ• λ„ˆν…Œμ΄μ…˜μ€ μœ„μ˜ 문제λ₯Ό ν•΄κ²°ν•΄ μ£ΌλŠ”λ°, JUnit도 버전 4λΆ€ν„° μ „λ©΄ λ„μž…ν•˜μ˜€λ‹€. 직접 μ œμž‘ν•œ μž‘은 ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬λ₯Ό μ΄μš©ν•˜μ—¬ μ• λ„ˆν…Œμ΄μ…˜μ˜ λ™μž‘ λ°©μ‹μ„ μ‚΄νŽ΄λ³΄μž.

/**
 * ν…ŒμŠ€νŠΈ λ©”μ„œλ“œμž„μ„ μ„ μ–Έν•˜λŠ” μ• λ„ˆν…Œμ΄μ…˜μ΄λ‹€.
 * λ§€κ°œλ³€μˆ˜ μ—†λŠ” 정적 λ©”μ„œλ“œ μ „μš©μ΄λ‹€.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}

  @Test μ• λ„ˆν…Œμ΄μ…˜ μ„ μ–Έ μžμ²΄μ—λ„ 두 κ°€μ§€μ˜ λ‹€λ₯Έ μ• λ„ˆν…Œμ΄μ…˜μ΄ 달렀 μžˆλŠ”λ°, 이처럼 μ• λ„ˆν…Œμ΄μ…˜ 선언에 λ‹€λ₯Έ μ• λ„ˆν…Œμ΄μ…˜μ„ λ©”νƒ€μ• λ„ˆν…Œμ΄μ…˜(meta-annotation)이라 ν•œλ‹€.

2κ°€μ§€ λ©”νƒ€μ• λ„ˆν…Œμ΄μ…˜μ€ λ‹€μŒμ„ μ˜λ―Έν•œλ‹€.

  1. @Retention(RetentionPolicy.RUNTIME)
    @Testκ°€ λŸ°νƒ€μž„에도 μœ μ§€λ˜μ–΄μ•Ό ν•œλ‹€.
  2. @Target(ElementType.METHOD)
    @Testκ°€ λ°˜λ“œμ‹œ λ©”μ„œλ“œ μ„ μ–Έμ—μ„œλ§Œ μ‚¬μš©λΌμ•Ό ν•œλ‹€.

  @Test μ• λ„ˆν…Œμ΄μ…˜μ€ 마컀(marker) μ• λ„ˆν…Œμ΄μ…˜μ΄λΌ ν•œλ‹€. 이 μ• λ„ˆν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λ©΄ ν”„λ‘œκ·Έλž˜λ¨Έκ°€ Test μ΄λ¦„에 μ˜€νƒ€λ₯Ό λ‚΄κ±°λ‚˜ λ©”μ„œλ“œ μ„ μ–Έ μ™Έμ˜ ν”„λ‘œκ·Έλž¨ μš”μ†Œμ— λ‹¬λ©΄ μ»΄νŒŒμΌ μ˜€λ₯˜λ₯Ό λ‚΄μ€€λ‹€.

 

  μ£Όμ„μœΌλ‘œ "λ§€κ°œλ³€μˆ˜ μ—†λŠ” 정적 λ©”μ„œλ“œ μ „μš©μ΄λ‹€"라고 μ“°μ—¬μžˆλ‹€. 이 μ œμ•½μ„ μ»΄νŒŒμΌλŸ¬κ°€ κ°•μ œν•  μˆ˜ μ—†μ–΄μ„œ, μ»΄νŒŒμΌμ€ μž˜ λ˜κ² μ§€λ§Œ ν…ŒμŠ€νŠΈ λ„ꡬλ₯Ό μ‹€ν–‰ν•  λ•Œ λ¬Έμ œκ°€ λœλ‹€.
  μ»΄νŒŒμΌλŸ¬κ°€ κ°•μ œν•˜λ„λ‘ ν•˜λ €λ©΄ javax.annotation.processing API λ¬Έμ„œλ₯Ό μ°Έκ³  μ μ ˆν•œ μ• λ„ˆν…Œμ΄μ…˜ 처리기λ₯Ό 직접 κ΅¬ν˜„ν•΄μ•Ό ν•œλ‹€.

 

@Testλ₯Ό μ‹€μ œ μ‚¬μš©ν•œ μ½”λ“œλŠ” μ•„λž˜μ™€ κ°™λ‹€.

public class Sample {
    @Test
    public static void m1() {
        // 성곡해야 ν•œλ‹€.
    }

    public static void m2() { }

    @Test
    public static void m3() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€.
        throw new RuntimeException("μ‹€νŒ¨");
    }

    public static void m4() { }

    @Test
    public void m5() {
        // 잘λͺ» μ‚¬μš©ν•œ 예: 정적 λ©”μ„œλ“œκ°€ μ•„λ‹ˆλ‹€.
    }

    public static void m6() { }

    @Test
    public static void m7() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€.
        throw new RuntimeException("μ‹€νŒ¨");
    }

    public static void m8() { }
}

public static void main(String[] args) throws Exception {
    int tests = 0;
    int passed = 0;
    Class<?> testClass = Class.forName("com.example.demo.item39.Sample");
    for(Method m : testClass.getDeclaredMethods()) {
        if(m.isAnnotationPresent(Test.class)) {
            tests++;
            try {
                m.invoke(null);
                passed++;
            } catch (InvocationTargetException wrappedExc) {
                Throwable exc = wrappedExc.getCause();
                System.out.println(m + " μ‹€νŒ¨: " + exc);
            } catch (Exception exc) {
                System.out.println("잘λͺ» μ‚¬μš©ν•œ @Test: " + m);
            }
        }
    }

    System.out.printf("성곡: %d, μ‹€νŒ¨: %d%n",
                        passed, tests - passed);
    // 좜λ ₯ κ²°κ³Ό
    // public static void com.example.demo.item39.Sample.m3() μ‹€νŒ¨: java.lang.RuntimeException: μ‹€νŒ¨
    // public static void com.example.demo.item39.Sample.m7() μ‹€νŒ¨: java.lang.RuntimeException: μ‹€νŒ¨
    // 잘λͺ» μ‚¬μš©ν•œ @Test: public void com.example.demo.item39.Sample.m5()
    // 성곡: 1, μ‹€νŒ¨: 3
}

  InvocationTargetException은 m3()κ³Ό m7()μ—μ„œ λ°œμƒν•œλ‹€.

  @Test μ• λ„ˆν…Œμ΄μ…˜μ„ 잘 λͺ» μ‚¬μš©ν•œ 경우 InvocationTargetException μ™Έμ˜ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λŠ”λ°, m5()μ—μ„œ λ°œμƒν•œλ‹€.

 

 

πŸ“™ μ˜ˆμ‹œ 2 - λ§€κ°œλ³€μˆ˜ ν•˜λ‚˜λ₯Ό λ°›λŠ” μ• λ„ˆν…Œμ΄μ…˜

  νŠΉμ • μ˜ˆμ™Έλ₯Ό λ˜μ Έμ•Όλ§Œ μ„±κ³΅ν•˜λŠ” ν…ŒμŠ€νŠΈλ₯Ό μ‚΄νŽ΄λ³΄μž.

/**
 * λͺ…μ‹œν•œ μ˜ˆμ™Έλ₯Ό λ˜μ €μ•Όλ§Œ μ„±κ³΅ν•˜λŠ” ν…ŒμŠ€νŠΈ λ©”μ„œλ“œμš© μ• λ„ˆν…Œμ΄μ…˜
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
    Class<? extends Throwable> value();
}

  이 μ• λ„ˆν…Œμ΄μ…˜μ˜ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ Class<? extends Throwable>이닀.

public class Sample2 {
    @ExceptionTest(ArithmeticException.class)
    public static void m1() {
        // 성곡해야 ν•œλ‹€.
        int i = 0;
        i = i / i;
    }

    @ExceptionTest(ArithmeticException.class)
    public static void m2() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€. (λ‹€λ₯Έ μ˜ˆμ™Έ λ°œμƒ)
        int[] a = new int[0];
        int i = a[1];
    }

    @ExceptionTest(ArithmeticException.class)
    public static void m3() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€. (μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ§€ μ•ŠμŒ)
    }
}

public static void main(String[] args) throws Exception {
    int tests = 0;
    int passed = 0;
    Class<?> testClass = Class.forName("com.example.demo.item39.Sample2");
    for(Method m : testClass.getDeclaredMethods()) {
        if(m.isAnnotationPresent(ExceptionTest.class)) {
            tests++;
            try {
                m.invoke(null);
                System.out.printf("ν…ŒμŠ€νŠΈ %s μ‹€νŒ¨: μ˜ˆμ™Έλ₯Ό λ˜μ§€μ§€ μ•ŠμŒ%n", m);
            } catch (InvocationTargetException wrappedExc) {
                Throwable exc = wrappedExc.getCause();
                Class<? extends Throwable> excType =
                    m.getAnnotation(ExceptionTest.class).value();
                if(excType.isInstance(exc)) {
                    passed++;
                } else {
                    System.out.printf(
                        "ν…ŒμŠ€νŠΈ %s μ‹€νŒ¨: κΈ°λŒ€ν•œ μ˜ˆμ™Έ %s,%n    λ°œμƒν•œ μ˜ˆμ™Έ %s%n",
                            m, excType.getName(), exc);
                }
            } catch (Exception exc) {
                System.out.println("잘λͺ» μ‚¬μš©ν•œ @ExceptionTest: " + m);
            }
        }
    }

    System.out.printf("성곡: %d, μ‹€νŒ¨: %d%n",
        passed, tests - passed);

    // 좜λ ₯ κ²°κ³Ό
    // ν…ŒμŠ€νŠΈ public static void com.example.demo.item39.Sample2.m2() μ‹€νŒ¨: κΈ°λŒ€ν•œ μ˜ˆμ™Έ java.lang.ArithmeticException,
    //    λ°œμƒν•œ μ˜ˆμ™Έ java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 0
    // ν…ŒμŠ€νŠΈ public static void com.example.demo.item39.Sample2.m3() μ‹€νŒ¨: μ˜ˆμ™Έλ₯Ό λ˜μ§€μ§€ μ•ŠμŒ
    // 성곡: 1, μ‹€νŒ¨: 2
}

  이 μ½”λ“œλŠ” μ• λ„ˆν…Œμ΄μ…˜ λ§€κ°œλ³€μˆ˜μ˜ 값을 μΆ”μΆœν•˜μ—¬ ν…ŒμŠ€νŠΈ λ©”μ„œλ“œκ°€ μ˜¬λ°”λ₯Έ μ˜ˆμ™Έλ₯Ό λ˜μ§€λŠ”μ§€ ν™•μΈν•œλ‹€. Sample2 ν΄λž˜μŠ€μ˜ μ£Όμ„μ—μ„œ ν™•인할 μˆ˜ μžˆλ“―이 1κ°œκ°€ μ„±κ³΅ν•˜κ³ , 2κ°œκ°€ μ‹€νŒ¨ν•œλ‹€.

 

 

πŸ“™ μ˜ˆμ‹œ 3 - λ°°μ—΄ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›λŠ” μ• λ„ˆν…Œμ΄μ…˜ νƒ€μž…

πŸ”Ž 1. 배열을 μ΄μš©ν•˜λŠ” 방법

  μ§€κΈˆκΉŒμ§€ μ˜ˆμ™Έ 1개λ₯Ό λ°›λŠ” μ• λ„ˆν…Œμ΄μ…˜μ„ μ‚΄νŽ΄λ³΄μ•˜λ‹€. μ•„λž˜ μ• λ„ˆν…Œμ΄μ…˜κ³Ό κ°™μ΄ 'μ˜ˆμ™Έλ₯Ό μ—¬λŸ¬ κ°œ λͺ…μ‹œν•˜κ³  κ·Έμ€‘ ν•˜λ‚˜κ°€ λ°œμƒν•˜λ©΄ μ„±κ³΅'ν•˜κ²Œ λ§Œλ“€ μˆ˜λ„ μžˆλ‹€.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
    Class<? extends Throwable>[] value();
}

 

public class Sample2 {
    @ExceptionTest(ArithmeticException.class)
    public static void m1() {
        // 성곡해야 ν•œλ‹€.
        int i = 0;
        i = i / i;
    }

    @ExceptionTest(ArithmeticException.class)
    public static void m2() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€. (λ‹€λ₯Έ μ˜ˆμ™Έ λ°œμƒ)
        int[] a = new int[0];
        int i = a[1];
    }

    @ExceptionTest(ArithmeticException.class)
    public static void m3() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€. (μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ§€ μ•ŠμŒ)
    }

    // λ°°μ—΄ λ§€κ°œλ³€μˆ˜
    @ExceptionTest({ IndexOutOfBoundsException.class,
                        NullPointerException.class})
    public static void doubleBad() {
        // 성곡해야 ν•œλ‹€.
        List<String> list = new ArrayList<>();

        // μžλ°” API λͺ…세에 λ”°λ₯΄λ©΄ λ‹€μŒ λ©”μ„œλ“œλŠ” IndexOutOfBoundsExceptionμ΄λ‚˜
        // NullPointerException을 던질 수 μžˆλ‹€.
        list.addAll(5, null);
    }
}

public static void main(String[] args) throws Exception {
    int tests = 0;
    int passed = 0;
    Class<?> testClass = Class.forName("com.example.demo.item39.Sample2");
    for(Method m : testClass.getDeclaredMethods()) {
        if(m.isAnnotationPresent(ExceptionTest.class)) {
            tests++;
            try {
                m.invoke(null);
                System.out.printf("ν…ŒμŠ€νŠΈ %s μ‹€νŒ¨: μ˜ˆμ™Έλ₯Ό λ˜μ§€μ§€ μ•ŠμŒ%n", m);
            } catch (Throwable wrappedExc) {
                Throwable exc = wrappedExc.getCause();
                int oldPassed = passed;
                Class<? extends Throwable>[] excTypes =
                    m.getAnnotation(ExceptionTest.class).value();
                for(Class<? extends Throwable> excType : excTypes) {
                    if(excType.isInstance(exc)) {
                        passed++;
                        break;
                    }
                }
                if (passed == oldPassed) {
                    System.out.printf("ν…ŒμŠ€νŠΈ %s μ‹€νŒ¨: %s %n", m, exc);
                }
            }
        }
    }

    System.out.printf("성곡: %d, μ‹€νŒ¨: %d%n",
        passed, tests - passed);

    // ν…ŒμŠ€νŠΈ public static void com.example.demo.item39.Sample2.m2() μ‹€νŒ¨: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 0
    // ν…ŒμŠ€νŠΈ public static void com.example.demo.item39.Sample2.m3() μ‹€νŒ¨: μ˜ˆμ™Έλ₯Ό λ˜μ§€μ§€ μ•ŠμŒ
    // 성곡: 2, μ‹€νŒ¨: 2
}

  μ›μ†Œκ°€ μ—¬λŸΏμΈ 배열을 μ§€μ •ν•  λ•ŒλŠ” μœ„μ™€ 같이 μ›μ†Œλ“€μ„ μ€‘κ΄„ν˜Έλ‘œ 감싸고 μ‰Όν‘œλ‘œ κ΅¬λΆ„ν•΄μ£ΌκΈ°λ§Œ ν•˜λ©΄ λœλ‹€.

 

πŸ”Ž 2. @Repeatable λ©”νƒ€μ• λ„ˆν…Œμ΄μ…˜μ„ μ΄μš©ν•˜λŠ” 방법

  μžλ°” 8μ—μ„œλŠ” μ—¬λŸ¬ 개의 값을 λ°›λŠ” μ• λ„ˆν…Œμ΄μ…˜μ„ λ°°μ—΄ λ§€κ°œλ³€μˆ˜ λŒ€μ‹  @Repeatable λ©”νƒ€μ• λ„ˆν…Œμ΄μ…˜μ„ μ΄μš©ν•˜μ—¬ κ΅¬ν˜„ν•  수 μžˆλ‹€. ν•˜λ‚˜μ˜ ν”„λ‘œκ·Έλž¨ μš”μ†Œμ— @Repeatable을 단 μ• λ„ˆν…Œμ΄μ…˜μ„ μ—¬λŸ¬ 개 달 수 μžˆλ„λ‘ ν•˜λŠ” 것이닀.

 

  @Repeatable을 κ°€μ§„ μ• λ„ˆν…Œμ΄μ…˜μ„ κ΅¬ν˜„ν•  λ•ŒλŠ” μ•„λž˜μ˜ λ‚΄μš©μ„ μ£Όμ˜ν•΄μ„œ λ§Œλ“€μ–΄μ•Ό ν•œλ‹€.

  1. @Repeatable을 단 μ• λ„ˆν…Œμ΄μ…˜μ„ λ°˜ν™˜ν•˜λŠ” 'μ»¨ν…Œμ΄λ„ˆ μ• λ„ˆν…Œμ΄μ…˜'을 ν•˜λ‚˜ 더 μ •μ˜ν•˜κ³ , @Repeatable에 이 μ»¨ν…Œμ΄λ„ˆ μ• λ„ˆν…Œμ΄μ…˜μ˜ class 객체λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ 전달해야 ν•œλ‹€.
  2. μ»¨ν…Œμ΄λ„ˆ μ• λ„ˆν…Œμ΄μ…˜μ€ λ‚΄λΆ€ μ• λ„ˆν…Œμ΄μ…˜ νƒ€μž…μ˜ 배열을 λ°˜ν™˜ν•˜λŠ” value λ©”μ„œλ“œλ₯Ό μ •μ˜ν•΄μ•Ό ν•œλ‹€.
  3. μ»¨ν…Œμ΄λ„ˆ μ• λ„ˆν…Œμ΄μ…˜ νƒ€μž…μ—λŠ” μ μ ˆν•œ 보쑴 μ •μ±…(@Retention)κ³Ό 적용 λŒ€μƒ(@Target)을 λͺ…μ‹œν•΄μ•Ό ν•œλ‹€.
// 반볡 κ°€λŠ₯ν•œ μ• λ„ˆν…Œμ΄μ…˜
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(ExceptionTestContainer.class)
public @interface ExceptionTest {
    Class<? extends Throwable> value();
}

// μ»¨ν…Œμ΄λ„ˆ μ• λ„ˆν‹°μ΄μ…˜
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTestContainer {
    ExceptionTest[] value();
}

  isAnnotationPresent둜 μ»¨ν…Œμ΄λ„ˆ μ• λ„ˆν…Œμ΄μ…˜μ΄ λ‹¬λ ΈλŠ”μ§€ κ²€μ‚¬ν•œλ‹€λ©΄ 반볡 κ°€λŠ₯ μ• λ„ˆν…Œμ΄μ…˜μ„ ν•œ 번만 단 λ©”μ„œλ“œλŠ” λ¬΄μ‹œλœλ‹€. κ·Έλž˜μ„œ getAnnotationByType λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ λ°˜λ³΅ κ°€λŠ₯ μ• λ„ˆν…Œμ΄μ…˜μ˜ μˆ˜μ™€ μƒκ΄€μ—†μ΄ λͺ¨λ‘ κ²€μ‚¬ν•˜λ„둝 ν•΄μ•Ό ν•œλ‹€.

public class Sample2 {
    @ExceptionTest(ArithmeticException.class)
    public static void m1() {
        // 성곡해야 ν•œλ‹€.
        int i = 0;
        i = i / i;
    }

    @ExceptionTest(ArithmeticException.class)
    public static void m2() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€. (λ‹€λ₯Έ μ˜ˆμ™Έ λ°œμƒ)
        int[] a = new int[0];
        int i = a[1];
    }

    @ExceptionTest(ArithmeticException.class)
    public static void m3() {
        // μ‹€νŒ¨ν•΄μ•Ό ν•œλ‹€. (μ˜ˆμ™Έκ°€ λ°œμƒν•˜μ§€ μ•ŠμŒ)
    }

    // 반볡 κ°€λŠ₯ν•œ μ—λ„ˆν…Œμ΄μ…˜ μ‚¬μš©
    @ExceptionTest(IndexOutOfBoundsException.class)
    @ExceptionTest(NullPointerException.class)
    public static void doubleBad() {
        // 성곡해야 ν•œλ‹€.
        List<String> list = new ArrayList<>();

        // μžλ°” API λͺ…세에 λ”°λ₯΄λ©΄ λ‹€μŒ λ©”μ„œλ“œλŠ” IndexOutOfBoundsExceptionμ΄λ‚˜
        // NullPointerException을 던질 수 μžˆλ‹€.
        list.addAll(5, null);
    }
}

public static void main(String[] args) throws Exception {
    int tests = 0;
    int passed = 0;
    Class<?> testClass = Class.forName("com.example.demo.item39.Sample2");
    for(Method m : testClass.getDeclaredMethods()) {
        if(m.isAnnotationPresent(ExceptionTest.class)
            || m.isAnnotationPresent(ExceptionTestContainer.class)) {
            tests++;
            try {
                m.invoke(null);
                System.out.printf("ν…ŒμŠ€νŠΈ %s μ‹€νŒ¨: μ˜ˆμ™Έλ₯Ό λ˜μ§€μ§€ μ•ŠμŒ%n", m);
            } catch (Throwable wrappedExc) {
                Throwable exc = wrappedExc.getCause();
                int oldPassed = passed;
                ExceptionTest[] excTests =
                    m.getAnnotationsByType(ExceptionTest.class);

                for(ExceptionTest excTest : excTests) {
                    if(excTest.value().isInstance(exc)) {
                        passed++;
                        break;
                    }
                }
                if (passed == oldPassed) {
                    System.out.printf("ν…ŒμŠ€νŠΈ %s μ‹€νŒ¨: %s %n", m, exc);
                }
            }
        }
    }

    System.out.printf("성곡: %d, μ‹€νŒ¨: %d%n",
        passed, tests - passed);

    // 좜λ ₯ κ²°κ³Ό
    // ν…ŒμŠ€νŠΈ public static void com.example.demo.item39.Sample2.m2() μ‹€νŒ¨: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 0 
    // ν…ŒμŠ€νŠΈ public static void com.example.demo.item39.Sample2.m3() μ‹€νŒ¨: μ˜ˆμ™Έλ₯Ό λ˜μ§€μ§€ μ•ŠμŒ
    // 성곡: 2, μ‹€νŒ¨: 2
}

  반볡 κ°€λŠ₯ μ• λ„ˆν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜λ©΄ μ½”λ“œμ˜ 가독성을 높일 수 μžˆλ‹€.

  ν•˜μ§€λ§Œ μ• λ„ˆν…Œμ΄μ…˜μ„ μ„ μ–Έν•˜κ³  이λ₯Ό μ²˜λ¦¬ν•˜λŠ” λΆ€λΆ„μ—μ„œλŠ” μ½”λ“œ 양이 λŠ˜μ–΄λ‚˜λ©°, 특히 처리 μ½”λ“œκ°€ λ³΅μž‘ν•΄μ Έ 였λ₯˜κ°€ λ‚  κ°€λŠ₯성이 컀짐을 λͺ…심해야 ν•œλ‹€.

 


  μ• λ„ˆν…Œμ΄μ…˜μœΌλ‘œ ν•  수 μžˆλŠ” 일을 λͺ…λͺ… νŒ¨ν„΄μœΌλ‘œ μ²˜λ¦¬ν•  μ΄μœ λŠ” μ—†λ‹€.

  λ‹€λ₯Έ ν”„λ‘œκ·Έλž˜λ¨Έκ°€ μ†ŒμŠ€μ½”λ“œμ— μΆ”κ°€ 정보λ₯Ό μ œκ³΅ν•  수 μžˆλŠ” 도ꡬλ₯Ό λ§Œλ“œλŠ” 일을 ν•œλ‹€λ©΄ μ λ‹Ήν•œ μ• λ„ˆν…Œμ΄μ…˜ νƒ€μž…λ„ ν•¨κ»˜ μ •μ˜ν•΄ μ œκ³΅ν•˜λ„λ‘ ν•˜μž.

 

ν•΄λ‹Ή 글은 Joshua Block λ‹˜μ˜ 'Effective Java 3/E'λ₯Ό μ°Έκ³ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

'πŸ“‚ JAVA > μ΄νŽ™ν‹°λΈŒ μžλ°”' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

μ •μ˜ν•˜λ €λŠ” 것이 νƒ€μž…μ΄λΌλ©΄ 마컀 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ41)]  (0) 2023.07.09
@Override μ• λ„ˆν…Œμ΄μ…˜μ„ μΌκ΄€λ˜κ²Œ μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ40)]  (0) 2023.07.09
ν™•μž₯ν•  수 μžˆλŠ” μ—΄κ±° νƒ€μž…μ΄ ν•„μš”ν•˜λ©΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ38)]  (0) 2023.07.05
ordinal 인덱싱 λŒ€μ‹  EnumMap을 μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ37)]  (0) 2023.07.02
λΉ„νŠΈ ν•„λ“œ λŒ€μ‹  EnumSet을 μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ36)]  (1) 2023.07.01
    'πŸ“‚ JAVA/μ΄νŽ™ν‹°λΈŒ μžλ°”' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
    • μ •μ˜ν•˜λ €λŠ” 것이 νƒ€μž…μ΄λΌλ©΄ 마컀 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ41)]
    • @Override μ• λ„ˆν…Œμ΄μ…˜μ„ μΌκ΄€λ˜κ²Œ μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ40)]
    • ν™•μž₯ν•  수 μžˆλŠ” μ—΄κ±° νƒ€μž…μ΄ ν•„μš”ν•˜λ©΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ38)]
    • ordinal 인덱싱 λŒ€μ‹  EnumMap을 μ‚¬μš©ν•˜λΌ - [6μž₯. μ—΄κ±° νƒ€μž…κ³Ό μ• λ„ˆν…Œμ΄μ…˜(μ•„μ΄ν…œ37)]
    Amenable
    Amenable
    CS, μžλ°”, 자료ꡬ쑰, μ•Œκ³ λ¦¬μ¦˜, μŠ€ν”„λ§, μŠ€ν”„λ§ λΆ€νŠΈμ— ν•΄λ‹Ήν•˜λŠ” κ°œλ°œμ— κ΄€ν•œ λ‚΄μš©μ„ κ³΅μœ ν•©λ‹ˆλ‹€.

    ν‹°μŠ€ν† λ¦¬νˆ΄λ°”