列挙タイプとエネルギー(5)
30131 ワード
[アイテム39]。名前付きモードではなくアニメーションを使用
従来,ツールやフレームワークに特別な処理が必要なプログラム要素には,完全に区別されたネーミングモードが採用されている.
public class HelloTest extends TestCase {
public void testGetUsers() {
return getUsers();
}
}
しかし、このネーミングモードにはいくつかの欠点があります.1つ目は誤字ではいけません
正しいプログラム要素のみで使用することは保証できません.たとえば、クラス名をTestSafetyMechanicalと名付けてJUnitに投げ込むと、JUnitは警告も予想されるテストも実行しません.
第三に、プログラム要素をパラメータとして伝達する適切な方法はない.テストが特定の例外を投げ出さなければ成功しないと仮定した場合、例外の名前をテストメソッド名に追加できますが、この方法は面白くないし、破壊されやすいです.コンパイラは、メソッド名に追加された文字列が例外を指すかどうか分かりません.
/**
* 테스트 메서드임을 선언하는 애너테이션이다.
* 매개변수 없는 정적 메서드 전용이다.
*/
@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拡張クラスを表すクラスオブジェクトで、すべての例外(およびエラー)タイプを受け入れます.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() { } // 실패해야 한다. (예외가 발생하지 않음)
}
この例外テストの例では、複数の例外をさらに指定し、そのうちの1つが発生したときに成功させることもできます.@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ExceptionTest {
Class<? extends Exception>[] value();
}
配列パラメータを受け入れる補助構文は非常に柔軟です.個々の要素配列を最適化したが、前の@Exception Testも変更されていない.@ExceptionTest({IndexOutOfBoundsException.class, NullPointerException.class})
public static void doublyBad() { // 성공해야 한다.
List<String> list = new ArrayList<>();
// 자바 API 명세에 따르면 다음 메서드는 IndexOutOfBoundsException이나
// NullPointerException을 던질 수 있다.
list.addAll(5, null);
}
Java 8では、複数の値のプレゼンテーションを他の方法で作成できます.これは配列パラメータではなく、コメントに@Repeatableメタデータを使用する方法です.@Repeatable付きのプレゼンテーションは、1つのプログラム要素に複数回追加できます.
@Repeatableには少し注意が必要です.
また、
@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();
}
繰り返し可能なアニメーションを使用すると、1つのプログラム要素に同じアニメーションを複数回追加すると、コードの読み取り可能性が高くなります.他のプログラマがソースコードに追加情報を提供するツールを作成している場合は、適切なプレゼンテーションタイプを定義し、提供します.アニメーションでできることをネーミングモードで処理する理由はありません.
Javaプログラマーの場合は、Javaが提供するプレゼンテーションタイプを使用します.
Reference
この問題について(列挙タイプとエネルギー(5)), 我々は、より多くの情報をここで見つけました https://velog.io/@roo333/Effective-Java-열거타입과-에너테이션4-te0mzggaテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol