何为注解

也称为元数据,为在代码中添加信息提供一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。将元数据和源代码二建结合起来,而不是保存在外部文档中。能够提供更加干净易读的代码以及编译期类型检查。

内置注解

  • @Override:表示当前方法定义将覆盖超类中的方法
  • @Deprecated:使用此注解,编译器会发出警告信息
  • @SuppressWarnings:关闭不当的编译器警告信息
  • @Target:定义注解应用在什么地方,一个方法或者一个域。下面列出可能的ElementType参数:
    • CONSTRUCTOR: 构造器声明
    • FIELD: 域声明
    • LOCAL_VARIABLE: 局部变量声明
    • METHOD: 方法声明
    • PACKAGE :包声明
    • PARAMETER: 参数声明
    • TYPE: 类、接口或enum声明
  • @Retention:定义注解在哪个级别可用,源码还是类文件还是运行时。可选RetentionPolicy参数为:
    • SOURCE: 注释被编译器丢弃
    • CLASS: 注释在class中可用,会被VM丢弃
    • RUNTIME:VM在运行期也保留注解,可以通过反射机制读取注解的信息。
  • Documented:将此注解包含在javadoc中
  • Inherited:允许子类继承父类中的注解

注解的定义及使用

注解的定义如下图所示,看起来像接口的方法,唯一的区别是可以指定默认值。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface useCase {
    public int id();
    public String description() default "no description";
}

注解使用时为名-值对的形式,例如下图中调用useCase的注解,我们可以很方便地看出下面每一个方法对应的功能描述,从而避免了另外书写代码文档。

public class PasswordUtils {
    @useCase(id=47,description = "password must contain at least one numeric")
    public boolean validatePassword(String password){
        return (password.matches("\\w*\\d\\w*"));
    }

    @useCase(id=48)
    public String encryptPassword(String password){
        return new StringBuilder(password).reverse().toString();
    }

    @useCase(id=49, description = "New passwords can't equal previously used ones")
    public boolean checkForNewPassword(List<String> prevPasswords, String password){
        return !prevPasswords.contains(password);
    }
}

注解处理器

如果单单是上文中展示的注解的话,我们通过普通的注释就可以实现这种解释代码的功能。所以关键的优点就在于,可以结合反射机制使用注解处理器帮助程序员解析带有注解的java源代码。

什么意思呢,通俗一点讲就是。如果一个项目有1,2,3,4,5共五个功能点,而程序员实现了1,2,3共3个功能点,并在其上面添加注解。如果使用普通注释的话,主管在检查代码时,需要查阅源代码,一个一个看功能点是否实现,非常费时。那如果我们使用注解,我们可以结合反射机制,通过代码直接快速检查功能点是否存在。

如下面的代码所示,假设我们在最初的设计文档中设计了47,48,49,50一共5个函数功能点,那么通过获取所有的annotation列表,直接排查就可以得到我们还有哪个功能点暂未实现。
代码中,第3-9行将找到的useCase从列表中依次去除,最后10-12行将列表中还存在的useCase输出,表明是没有找到的case。
cl.getDeclaredMethods()获得类中声明的所有方法,m.getAnnotation获取方法中对应的注解。

public class UseCaseTracker {
    public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
        for(Method m: cl.getDeclaredMethods()){
            useCase uc = m.getAnnotation(useCase.class);
            if(uc != null){
                System.out.println("Found Use Case: " + uc.id() + " " + uc.description());
                useCases.remove(new Integer(uc.id()));
            }
        }
        for(int i : useCases){
            System.out.println("Waning: Missing use case-"+i);
        }
    }
    public static void main(String[] args){
        List<Integer> useCases = new ArrayList<Integer>();
        Collections.addAll(useCases,47,48,49,50);
        trackUseCases(useCases,PasswordUtils.class);
    }
}

注解元素

注解内的元素包括:

  • 所有基本类型:int,float,boolean等等
  • String
  • Class
  • enum
  • Annotation
  • 以上类型的数组
    不存在于上述类型中的类型出现时,编译就会报错。注解也可以嵌套。

默认值

注解的默认值不能有不确定的值,要么是默认值要么是注解时提供的值。不能以null作为其值,如果想要表示某个元素不存在,可以自定义一些特殊的值。

基于注解的单元测试

单元测试是注解的一个重要功能,最常用的为@Test注解。@Test标记测试方法,不带参数,返回值为boolean类型来表示测试成功与否。

优点

  • 无需创建单独的类来保存单元测试,可以直接在需要验证的类中编写测试
  • 不仅能测试public方法,还能测试private方法

一个简单的单元测试如下:

import net.mindview.atunit.Test;
public int methodOne(){
    return 1;
}
@Test
boolean testOne(){
    return methodOne()==1;
}
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐