(Thinking in Java)第20章 註解

註解是向代碼中添加信息的一種方法,而且在以後還可使用這些數據
就好比這個方法是用來剝香蕉的,可是咱們看就是一串代碼,咱們沒辦法在代碼裏寫一段指令說「我這個程序是用來剝香蕉的」,固然除了註釋。而這能夠經過註解辦到,在代碼中以Java指令語言的形式化方法來爲代碼提供更多信息。java

1、基本語法

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 {}

clipboard.png

@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*\\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);
    }
}

這是個註解的簡單應用,經過這些註解,咱們在閱讀代碼的時候能夠更清晰的獲取更多信息數據庫

2.元註解

clipboard.png

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<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("Warning: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);
    }
}

@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*\\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語言之一,也正是由於有能夠讀取註解的方法,因此註解才強於直接註釋。
在上面的例子中咱們能夠看出,能夠將各個方法的註解信息讀取出來app

1.註解元素

就像以前的id和dicription同樣,這叫作註解元素,可用類型以下:
clipboard.pngide

2.默認值的限制

clipboard.png
就是註解元素必須有一個確認的值,沒有不行,空也不行,那假如就是空的就是不存在咋整呢。
因此咱們只能定義一些特殊的值來解決單元測試

@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;
}

這樣就用一個註解就能夠套上各類類型,不過這樣就不能針對不一樣類型設計具備針對性的註解元素了ui

咱們還能夠爲一個元素打上倆註解,這沒什麼,就是亂了點this

4.註解不支持繼承

不讓繼承,別逼逼spa

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<String> columnDefs = new ArrayList<String>();

        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("\n\t" + columnDef + ",");
        }
        String tableCreate = createCommand.substring(0,
                createCommand.length() - 1) + ");";
        System.out.println("Table Creation SQL for" + Member.class + " is:\n"
                + 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語句。也就是說把註解信息提取了出來。設計

3、使用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<MethodDeclaration> interfaceMethods = new ArrayList<MethodDeclaration>();
    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<AnnotationTypeDeclaration> atds,
            AnnotationProcessorEnvironment env) {
        return new InterfaceExtractorProcessor(env);
    }

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

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

}

4、沒看懂= =真的

5、基於註解的單元測試

QNMLGB的不看了老子

end

相關文章
相關標籤/搜索