资讯专栏INFORMATION COLUMN

(Thinking in Java)第20章 注解

刘玉平 / 475人阅读

摘要:而这可以通过注解办到,在代码中以指令语言的形式化方法来为代码提供更多信息。有注解的说明这个成员变量是一个列名,然后根据注解信息来生成相应的语句。也就是说把注解信息提取了出来。

注解是向代码中添加信息的一种方法,并且在之后还可以使用这些数据
就比如这个方法是用来剥香蕉的,但是我们看就是一串代码,我们没办法在代码里写一段指令说“我这个程序是用来剥香蕉的”,当然除了注释。而这可以通过注解办到,在代码中以Java指令语言的形式化方法来为代码提供更多信息。

一、基本语法 1.定义注解
package tij.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class AnnotationTest {

}
class Testable {
    void execute() {
        System.out.println("Executing...");
    }
    @Test
    void testExecute() {
        execute();
    }
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}

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

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

    @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 prevPasswords,
            String password) {
        return !prevPasswords.contains(password);
    }
}

这是个注解的简单应用,通过这些注解,我们在阅读代码的时候可以更清晰的获取更多信息

2.元注解

二、编写注解处理器
package tij.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class AnnotationTest {
    public static void trackUseCases(List 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("Warning:Missing use case-" + i);
        }
    }

    public static void main(String[] args) {
        List useCases = new ArrayList();
        Collections.addAll(useCases, 47, 48, 49, 50);
        trackUseCases(useCases, PasswordUtils.class);
    }
}

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

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

    @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 prevPasswords,
            String password) {
        return !prevPasswords.contains(password);
    }
}

因为注解是Java语言之一,也正是因为有可以读取注解的方法,所以注解才强于直接注释。
在上面的例子中我们可以看出,可以将各个方法的注解信息读取出来

1.注解元素

就像之前的id和dicription一样,这叫做注解元素,可用类型如下:

2.默认值的限制


就是注解元素必须有一个确认的值,没有不行,空也不行,那假如就是空的就是不存在咋整呢。
所以我们只能定义一些特殊的值来解决

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface SimulatingNull{
    public int id() default -1;
    public String decription() default "";
}
3.生成外部文件
package tij.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class AnnotationTest {

    public static void main(String[] args) {}
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DBTable {
    public String name() default "";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Constraints {
    boolean primaryKey() default false;
    boolean allowNull() default true;
    boolean unique() default false;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface SQLString {
    int value() default 0;
    String name() default "";
    Constraints constraints() default @Constraints;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface SQLInteger {
    String name() default "";
    Constraints constraints() default @Constraints;
}

@DBTable(name = "MEMBER")
class Member {
    @SQLString(30)
    String firstName;
    @SQLString(50)
    String lastName;
    @SQLString
    Integer age;
    @SQLString(value = 30, constraints = @Constraints(primaryKey = true))
    String handle;
    static int memberCount;
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public Integer getAge() {
        return age;
    }
    public String getHandle() {
        return handle;
    }
    public String toString() {
        return handle;
    }
    
}

其实这段代码很好的举了个注解运用的例子,
@Target(ElementType.TYPE)这个东西告诉编译器,这条注解要用在类声明上,就像DBTable一样,这个注解放在了class Member之前
@Target(ElementType.FIELD)这个东西告诉编译器,这条注解要放在域声明之前。
另外,如果只有一个名字叫做value的元素需要赋值的时候,就不需要“key=value”这种赋值,不用key,默认的。
不过,为了每个类型都设计一个注解未免太麻烦了,我们可以这么做

enum Type{
    String,Integer,Float;
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface TableColumn {
    Type type() default Type.String;
    Constraints constraints() default @Constraints;
}

这样就用一个注解就可以套上各种类型,不过这样就不能针对不同类型设计具有针对性的注解元素了

我们还可以为一个元素打上俩注解,这没什么,就是乱了点

4.注解不支持继承

不让继承,别逼逼

5.实现处理器
package tij.annotation;

import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class AnnotationTest {

    public static void main(String[] args) {
        DBTable dbTable = Member.class.getAnnotation(DBTable.class);
        String tableName = dbTable.name();
        List columnDefs = new ArrayList();

        for (Field field : Member.class.getFields()) {
            System.out.println(field.getName());
            String columnName = null;
            Annotation[] anns = field.getDeclaredAnnotations();
            if (anns.length < 1)
                continue;// 没检测得到注解,说明这个参数并不是一个数据库表的列
            if (anns[0] instanceof SQLInteger) {
                SQLInteger sInt = (SQLInteger) anns[0];
                if (sInt.name().length() < 1)
                    columnName = field.getName();
                else
                    columnName = sInt.name();
                columnDefs.add(columnName + " INT"
                        + getConstraints(sInt.constraints()));
            }
            if (anns[0] instanceof SQLString) {
                SQLString sString = (SQLString) anns[0];
                if (sString.name().length() < 1)
                    columnName = field.getName();
                else
                    columnName = sString.name();
                columnDefs.add(columnName + " VARCHAR(" + sString.value() + ")"
                        + getConstraints(sString.constraints()));
            }
        }
        StringBuilder createCommand = new StringBuilder(
                "CREATE TABLE " + tableName + "(");
        for (String columnDef : columnDefs) {
            createCommand.append("
	" + columnDef + ",");
        }
        String tableCreate = createCommand.substring(0,
                createCommand.length() - 1) + ");";
        System.out.println("Table Creation SQL for" + Member.class + " is:
"
                + tableCreate);
    }

    private static String getConstraints(Constraints con) {
        String constraints = "";
        if (!con.allowNull())
            constraints += " NOT NULL ";
        if (con.primaryKey())
            constraints += " PRIMARY KEY ";
        if (con.unique())
            constraints += " UNIQUE ";
        return constraints;
    }
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DBTable {
    public String name() default "";
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Constraints {
    boolean primaryKey() default false;
    boolean allowNull() default true;
    boolean unique() default false;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface SQLString {
    int value() default 0;
    String name() default "";
    Constraints constraints() default @Constraints;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface SQLInteger {
    String name() default "";
    Constraints constraints() default @Constraints;
}

@DBTable(name = "MEMBER")
class Member {
    @SQLString(30)
    public String firstName;
    @SQLString(50)
    public String lastName;
    @SQLString
    public Integer age;
    @SQLString(value = 30, constraints = @Constraints(primaryKey = true))
    public String handle;
    static int memberCount;
    public String getFirstName() {
        return firstName;
    }
    public String getLastName() {
        return lastName;
    }
    public Integer getAge() {
        return age;
    }
    public String getHandle() {
        return handle;
    }
    public String toString() {
        return handle;
    }

}

这段代码超棒,实际的告诉你这些注解到底怎么用,有什么用。有注解的说明这个成员变量是一个列名,然后根据注解信息来生成相应的SQL语句。也就是说把注解信息提取了出来。

三、使用apt处理注解

说实话,没看懂

package tij.annotation;

import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;

import com.sun.mirror.apt.AnnotationProcessor;
import com.sun.mirror.apt.AnnotationProcessorEnvironment;
import com.sun.mirror.apt.AnnotationProcessorFactory;
import com.sun.mirror.declaration.AnnotationTypeDeclaration;
import com.sun.mirror.declaration.MethodDeclaration;
import com.sun.mirror.declaration.ParameterDeclaration;
import com.sun.mirror.declaration.TypeDeclaration;

public class AnnotationTest {
    public static void main(String[] args) {}

}

@ExtractInterface("IMultipier")
class Multiplier {

    public int multiply(int x, int y) {
        int total = 0;
        for (int i = 0; i < x; i++) {
            total = add(total, y);
        }
        return total;
    }
    private int add(int x, int y) {
        return x + y;
    }
    public static void main(String[] args) {
        Multiplier m = new Multiplier();
        System.out.println("11*16=" + m.multiply(11, 16));
    }

}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
@interface ExtractInterface {
    public String value();
}

class InterfaceExtractorProcessor implements AnnotationProcessor {
    private final AnnotationProcessorEnvironment env;
    private ArrayList interfaceMethods = new ArrayList();
    public InterfaceExtractorProcessor(AnnotationProcessorEnvironment env) {
        this.env = env;
    }
    @Override
    public void process() {
        for (TypeDeclaration typeDec1 : env.getSpecifiedTypeDeclarations()) {
            ExtractInterface annot = typeDec1
                    .getAnnotation(ExtractInterface.class);
            if (annot == null)
                break;
            for (MethodDeclaration m : typeDec1.getMethods())
                if (m.getModifiers().contains(Modifier.STATIC)
                        && !(m.getModifiers().contains(Modifier.STATIC)))
                    interfaceMethods.add(m);
            if (interfaceMethods.size() > 0) {
                try {
                    PrintWriter writer = env.getFiler()
                            .createSourceFile(annot.value());
                    writer.println("package"
                            + typeDec1.getPackage().getQualifiedName() + ";");
                    writer.println("public interface " + annot.value() + " {");
                    for (MethodDeclaration m : interfaceMethods) {
                        writer.print(" public ");
                        writer.print(m.getReturnType() + " ");
                        writer.print(m.getSimpleName() + " (");
                        int i = 0;
                        for (ParameterDeclaration parm : m.getParameters()) {
                            writer.print(parm.getType() + " "
                                    + parm.getSimpleName());
                            if (++i < m.getParameters().size())
                                writer.print(", ");
                        }
                        writer.println(");");
                    }
                    writer.println("}");
                    writer.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }
    }
}

class InterfaceExtractorProcessorFactory implements AnnotationProcessorFactory {

    @Override
    public AnnotationProcessor getProcessorFor(
            Set atds,
            AnnotationProcessorEnvironment env) {
        return new InterfaceExtractorProcessor(env);
    }

    @Override
    public Collection supportedAnnotationTypes() {
        return Collections.singleton("annotations.ExtractInterface");
    }

    @Override
    public Collection supportedOptions() {
        return Collections.emptySet();
    }

}
四、没看懂= =真的 五、基于注解的单元测试

QNMLGB的不看了老子

end

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/70182.html

相关文章

  • Thinking in Java11 持有对象

    摘要:迭代器解决了这个问题。删除后于是我们可以写一个方法,接受一个类型,然后让他调用方法,这就不需要考虑这个是个还是了,也就是说,可以将遍历容器的操作与序列底层的结构分离,迭代器统一了对容器类的访问方式。十二和两种遍历的方法,与迭代器方法。 一、泛型和类型安全的容器 package tij.hoding; import java.util.ArrayList; public class ...

    v1 评论0 收藏0
  • Thinking in Java13 字符串

    摘要:四上的操作看五格式化输出运用和语言很相似和是等价的哟类格式化说明符转换六正则表达式网上教程学七扫描输入新增了类。 一、不可变String String类型的对象是不可变的,所有的改变实际上都是创建了一个新的String对象,另外当String作为传入参数的时候,其实实际上传入的是这个引用的一个拷贝,这个方法结束了之后这个传入的引用也就消失了,原来的那个String不会受到方法内的影响而...

    feng409 评论0 收藏0
  • Thinking in Java14 类型信息

    摘要:通过运行时类型信息,程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。编程应该尽量面向接口编程,应该对类型信息尽量的少了解二对象看书,书上写得好静态语句块在这个类被加载的时候运行。 一、为什么需要RTTI Run-Time Type Information。通过运行时类型信息,程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。编程应该尽量...

    tomorrowwu 评论0 收藏0
  • Thinking in Java10 内部类

    摘要:内部类中也可以取得这个外部类对象引用。创建成员内部类对象的时候需要外部类对象。另外在方法中的内部类不能加等权限修饰符,只能加和修饰符。可以在接口内部定义内部类,而且他们即使没有修饰,也会自动变成的。 Thinking in Java捞干货,写笔记 一、成员内部类 1.最基本使用 public class Demo { class Contents{ privat...

    Brenner 评论0 收藏0
  • Thinking in Java9 接口

    摘要:但如果导出类还有抽象方法,那这个类还应该加上声明为抽象类。并且接口具有继承的一系列特点,如向上转型等等。接口中的方法是自动是的。 Thinking in Java 好书全是干货 一、抽象类和抽象方法 抽象方法:这种方法只有声明而没有方法体,下面是抽象方法生命所采用的语法 abstract void f(); 包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为...

    CoorChice 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<