item 39 incheol - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki
Effective Java 3e μμ΄ν 39λ₯Ό μμ½ν λ΄μ© μ λλ€.
μ ν΅μ μΌλ‘ λꡬλ νλ μμν¬κ° νΉλ³ν λ€λ€μΌ ν νλ‘κ·Έλ¨ μμμλ λ± κ΅¬λΆλλ λͺ λͺ ν¨ν΄μ μ μ©ν΄μλ€.
public class HelloTest extends TestCase {
public void testGetUsers() {
return getUsers();
}
}
κ·Έλ¬λ μ΄λ¬ν λͺ λͺ ν¨ν΄μ λͺκ°μ§ λ¨μ μ κ°μ§κ³ μλ€.
- 첫 λ²μ§Έ, μ€νκ° λλ©΄ μ λλ€.
- λ λ²μ§Έ, μ¬λ°λ₯Έ νλ‘κ·Έλ¨ μμμμλ§ μ¬μ© λλ¦¬λΌ λ³΄μ¦ν λ°©λ²μ΄ μλ€. μ컨λ ν΄λμ€ μ΄λ¦μ TestSafetyMechanismsλ‘ μ§μ΄ JUnitμ λμ Έ 쀬λ€κ³ νλ©΄ JUnitμ κ²½κ³ λ©μμ§μ‘°μ°¨ μΆλ ₯νμ§ μμΌλ©΄μ μλν ν μ€νΈκ° μ ν μνλμ§ μμ κ²μ΄λ€.
- μΈ λ²μ§Έ, νλ‘κ·Έλ¨ μμλ₯Ό λ§€κ° λ³μλ‘ μ λ¬ν λ§λ ν λ°©λ²μ΄ μλ€. νΉμ μμΈλ₯Ό λμ ΈμΌλ§ μ±κ³΅νλ ν μ€νΈκ° μλ€κ³ κ°μ νμ λ, μμΈμ μ΄λ¦μ ν μ€νΈ λ©μλ μ΄λ¦μ λ§λΆμ΄λ λ°©λ²λ μμ§λ§ 보기λ λμκ³ κΉ¨μ§κΈ°λ μ½λ€. μ»΄νμΌλ¬λ λ©μλ μ΄λ¦μ λ§λΆμΈ λ¬Έμμ΄μ΄ μμΈλ₯Ό κ°λ₯΄ν€λ μ§ μ λλ¦¬κ° μλ€.
μλν μ΄μ μ μ΄ λͺ¨λ λ¬Έμ λ₯Ό ν΄κ²°ν΄μ£Όλ κ°λ μΌλ‘ Junitλ λ²μ 4λΆν° μ λ©΄ λμ νμλ€.
/**
* ν
μ€νΈ λ©μλμμ μ μΈνλ μ λν
μ΄μ
μ΄λ€.
* 맀κ°λ³μ μλ μ μ λ©μλ μ μ©μ΄λ€.
*/
@Retention(RetentionPolicy.RUNTIME) // @Testκ° λ°νμμλ μ μ§λμ΄μΌ νλ€λ νμμ΄λ€.
@Target(ElementType.METHOD) // @Testκ° λ°λμ λ©μλ μ μΈμμλ§ μ¬μ©λΌμΌ νλ€.
public @interface Test { // @Test
}
λ§μ»€ μ λν μ΄μ μ λμνλ νλ‘κ·Έλ¨μ μμ±ν΄λ³΄μ.
public class RunTests {
public static void main(String[] args) throws Exception {
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName(args[0]);
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);
}
}
μ΄ ν μ€νΈ λ¬λλ λͺ λ Ήμ€λ‘λΆν° μμ μ κ·νλ ν΄λμ€ μ΄λ¦μ λ°μ, κ·Έ ν΄λμ€μμ @Test μλν μ΄μ μ΄ λ¬λ¦° λ©μλλ₯Ό μ°¨λ‘λ‘ νΈμΆνλ€.
λ€μμ νΉμ μμΈλ₯Ό λμ ΈμΌλ§ μ±κ³΅νλ ν μ€νΈλ₯Ό μ§μνλλ‘ ν΄λ³΄μ.
/**
* λͺ
μν μμΈλ₯Ό λμ ΈμΌλ§ μ±κ³΅νλ ν
μ€νΈ λ©μλμ© μ λν
μ΄μ
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
Class<? extends Throwable> value();
}
@ExceptionTest μ λ Έν μ΄μ μ λν λ΄λΆ ꡬνμ μλμ κ°μ΄ λμ΄ μλ€.
public class RunTests {
public static void main(String[] args) throws Exception {
int tests = 0;
int passed = 0;
Class<?> testClass = Class.forName(args[0]);
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);
}
}
if (m.isAnnotationPresent(ExceptionTest.class)) {
tests++;
try {
m.invoke(null);
System.out.printf("ν
μ€νΈ %s μ€ν¨: μμΈλ₯Ό λμ§μ§ μμ%n", m);
} catch (InvocationTargetException wrappedEx) {
Throwable exc = wrappedEx.getCause();
Class<? extends Throwable> excType =
m.getAnnotation(ExceptionTest.class).value();
if (excType.isInstance(exc)) {
passed++;
} else {
System.out.printf(
"ν
μ€νΈ %s μ€ν¨: κΈ°λν μμΈ %s, λ°μν μμΈ %s%n",
m, excType.getName(), exc);
}
} catch (Exception exc) {
System.out.println("μλͺ» μ¬μ©ν @ExceptionTest: " + m);
}
}
}
System.out.printf("μ±κ³΅: %d, μ€ν¨: %d%n",
passed, tests - passed);
}
}
Throwableμ νμ₯ν ν΄λμ€μ Class κ°μ²΄λΌλ λ»μ΄λ©° λͺ¨λ μμΈ(μ μ€λ₯) νμ μ λ€ μμ©νλ€.
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() { } // μ€ν¨ν΄μΌ νλ€. (μμΈκ° λ°μνμ§ μμ)
}
μ΄ μμΈ ν μ€νΈ μμμ ν κ±Έμ λ λ€μ΄κ°, μμΈλ₯Ό μ¬λ¬ κ° λͺ μνκ³ κ·Έμ€ νλκ° λ°μνλ©΄ μ±κ³΅νκ² λ§λ€ μλ μλ€.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
Class<? extends Exception>[] value();
}
λ°°μ΄ λ§€κ°λ³μλ₯Ό λ°λ μ λν μ΄μ μ© λ¬Έλ²μ μμ£Ό μ μ°νλ€. λ¨μΌ μμ λ°°μ΄μ μ΅μ ννμ§λ§, μμμ @ExceptionTestλ€λ λͺ¨λ μμ μμ΄ μμ©νλ€.
@ExceptionTest({IndexOutOfBoundsException.class, NullPointerException.class})
public static void doublyBad() { // μ±κ³΅ν΄μΌ νλ€.
List<String> list = new ArrayList<>();
// μλ° API λͺ
μΈμ λ°λ₯΄λ©΄ λ€μ λ©μλλ IndexOutOfBoundsExceptionμ΄λ
// NullPointerExceptionμ λμ§ μ μλ€.
list.addAll(5, null);
}
μλ° 8μμλ μ¬λ¬ κ°μ κ°μ λ°λ μ λν μ΄μ μ λ€λ₯Έ λ°©μμΌλ‘λ λ§λ€ μ μλ€. λ°°μ΄ λ§€κ°λ³μλ₯Ό μ¬μ©νλ λμ μ λν μ΄μ μ @Repeatable λ©ν μ λν μ΄μ μ λ€λ λ°©μμ΄λ€. @Repeatableμ λ¨ μ λν μ΄μ μ νλμ νλ‘κ·Έλ¨ μμμ μ¬λ¬ λ² λ¬ μ μλ€.
- Repeatableμ λ¨ μ λν μ΄μ μ λ°ννλ '컨ν μ΄λ μ λν μ΄μ 'μ νλ λ μ μνκ³ , @Repeatableμ 컨ν μ΄λ μ λν μ΄μ μ class κ°μ²΄λ₯Ό 맀κ°λ³μλ‘ μ λ¬ν΄μΌ νλ€.
- 컨ν μ΄λ μ λν μ΄μ μ λ΄λΆ μ λν μ΄μ νμ μ λ°°μ΄μ λ°ννλ value λ©μλλ₯Ό μ μν΄μΌ νλ€.
- 컨ν μ΄λ μλν μ΄μ νμ μλ μ μ ν 보쑴 μ μ± (@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();
}
λ°λ³΅ κ°λ₯ μ λν μ΄μ μ μ¬μ©ν΄ νλμ νλ‘κ·Έλ¨ μμμ κ°μ μ λν μ΄μ μ μ¬λ¬ λ² λ¬ λμ μ½λ κ°λ μ±μ λμ¬λ³΄μλ€. λ€λ₯Έ νλ‘κ·Έλλ¨Έκ° μμ€μ½λμ μΆκ° μ 보λ₯Ό μ 곡ν μ μλ λꡬλ₯Ό λ§λλ μΌμ νλ€λ©΄ μ λΉν μ λν μ΄μ νμ λ ν¨κ» μ μν΄ μ 곡νμ. μ λν μ΄μ μΌλ‘ ν μ μλ μΌμ λͺ λͺ ν¨ν΄μΌλ‘ μ²λ¦¬ν μ΄μ λ μλ€.
μλ° νλ‘κ·Έλλ¨ΈλΌλ©΄ μμΈ μμ΄ μλ°κ° μ 곡νλ μ λν μ΄μ νμ λ€μ μ¬μ©ν΄μΌ νλ€.