zoukankan      html  css  js  c++  java
  • Effective Java 35 Prefer annotations to naming patterns

    Disadvantages of naming patterns

    1. Typographical errors may result in silent failures.
    2. There is no way to ensure that they are used only on appropriate program elements.
    3. They provide no good way to associate parameter values with program elements.

    Naming patters example

    testXXX

    textXXXWithXXXException

       

    Better solution: meta-annotations

    // Marker annotation type declaration

    import java.lang.annotation.*;

    /**

    * Indicates that the annotated method is a test method.

    * Use only on parameter less static methods.

    */

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.METHOD)

    public @interface Test {

    }

    /**

    * Indicates that the annotated method is a test method that must throw the

    * designated exception to succeed.

    */

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.METHOD)

    public @interface ExceptionTest {

    Class<? extends Exception> value();

    }

       

    /**

    * Annotation type with an array parameter

    */

    @Retention(RetentionPolicy.RUNTIME)

    @Target(ElementType.METHOD)

    public @interface MultiExceptionTest {

    Class<? extends Exception>[] value();

    }

    1. The @Retention(RetentionPolicy.RUNTIME) meta-annotation indicates that Test annotations should be retained at runtime. Without it, Test annotations would be invisible to the test tool.
    2. The @Target(ElementType.METHOD)meta-annotation indicates that the Test annotation is legal only on method declarations: it cannot be applied to class declarations, field declarations, or other program elements.
    3. The parameter definition Class<? extends Exception> value(); and Class<? extends Exception>[] value(); declare the required one or more exceptions to be generated by the method.

    // Program containing marker annotations

    public class Sample {

    @Test public static void m1() { } // Test should pass

    public static void m2() { }

    @Test public static void m3() { // Test Should fail

    throw new RuntimeException("Boom");

    }

    public static void m4() { }

    @Test public void m5() { } // INVALID USE: nonstatic method

    public static void m6() { }

    @Test public static void m7() { // Test should fail

    throw new RuntimeException("Crash");

    }

    public static void m8() { }

    }

       

    /**

    * @author Kaibo

    * Program containing annotations with a parameter.

    */

    public class Sample2 {

    @ExceptionTest(ArithmeticException.class)

    public static void m1() { // Test should pass

    int i = 0;

    i = i / i;

    }

       

    @ExceptionTest(ArithmeticException.class)

    public static void m2() { // Should fail (wrong exception)

    int[] a = new int[0];

    int i = a[1];

    }

       

    @ExceptionTest(ArithmeticException.class)

    public static void m3() {

    } // Should fail (no exception)

    }

       

    /**

    * @author Kaibo

    *

    */

    public class Sample3 {

    // Code containing an annotation with an array parameter

    @MultiExceptionTest({ IndexOutOfBoundsException.class,

    NullPointerException.class })

    public static void doublyBad() {

    List<String> list = new ArrayList<String>();

    // The spec permits this method to throw either

    // IndexOutOfBoundsException or NullPointerException

    list.addAll(5, null);

    }

    }

       

    /**

    * @author Kaibo

    * Program to process marker annotations.

    */

    import java.lang.reflect.*;

       

    public class RunTests {

    public static void main(String[] args) throws Exception {

       

    Class<?> testClass = Class.forName(args[0]);

    testSample1(testClass);

    testClass = Class.forName(args[1]);

    testSample2(testClass);

    testClass = Class.forName(args[2]);

    testSample3(testClass);

    }

       

    private static void testSample1(Class<?> testClass) {

    int tests = 0;

    int passed = 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 + " failed: " + exc);

    } catch (Exception exc) {

    System.out.println("INVALID @Test: " + m);

    }

    }

    }

    System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);

    }

       

    /**

    * @param testClass

    */

    private static void testSample2(Class<?> testClass) {

    int tests = 0;

    int passed = 0;

    for (Method m : testClass.getDeclaredMethods()) {

    if (m.isAnnotationPresent(ExceptionTest.class)) {

    tests++;

    try {

    m.invoke(null);

    System.out.printf("Test %s failed: no exception%n", m);

    } catch (InvocationTargetException wrappedEx) {

    Throwable exc = wrappedEx.getCause();

    Class<? extends Exception> excType = m.getAnnotation(

    ExceptionTest.class).value();

    if (excType.isInstance(exc)) {

    passed++;

    } else {

    System.out.printf(

    "Test %s failed: expected %s, got %s%n", m,

    excType.getName(), exc);

    }

    } catch (Exception exc) {

    System.out.println("INVALID @Test: " + m);

    }

    }

    }

    System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);

    }

       

    private static void testSample3(Class<?> testClass) {

    int tests = 0;

    int passed = 0;

    for (Method m : testClass.getDeclaredMethods()) {

    if (m.isAnnotationPresent(ExceptionTest.class)) {

    tests++;

    try {

    m.invoke(null);

    System.out.printf("Test %s failed: no exception%n", m);

    } catch (Throwable wrappedExc) {

    Throwable exc = wrappedExc.getCause();

    Class<? extends Exception>[] excTypes = m.getAnnotation(

    MultiExceptionTest.class).value();

    int oldPassed = passed;

    for (Class<? extends Exception> excType : excTypes) {

    if (excType.isInstance(exc)) {

    passed++;

    break;

    }

    }

    if (passed == oldPassed)

    System.out.printf("Test %s failed: %s %n", m, exc);

    }

    }

    }

    System.out.printf("Passed: %d, Failed: %d%n", passed, tests - passed);

    }

    }

    Summary

    There is simply no reason to use naming patterns now that we have annotations. All programmers should, however, use the predefined annotation types provided by the Java platform(Item 36 and Item24).

  • 相关阅读:
    CPU 常识(计算机组成原理)
    设置动态网站,要求访问端口 8998
    未知高宽的div在其父级div中垂直居中显示
    ES6 学习 -- Generator函数
    ES6 学习 -- Class继承
    ES6 学习 -- Class
    ES6 学习 -- 字符串模板
    ES6 学习 -- 字符串新增方法
    ES6 学习 -- Promise对象
    ES6 学习 -- Set和Map数据结构
  • 原文地址:https://www.cnblogs.com/haokaibo/p/Prefer-annotations-to-naming-patterns.html
Copyright © 2011-2022 走看看