本章先介绍第一种前端编译器,也是大家都认可的编译器。
源码编译自OpenJdk7,从Sun Javac源码来看,编译大致可分为3个过程:
Java的泛型是典型的语法糖。
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "你好");
map.put("how are you?", "吃了没?");
System.out.println(map.get("hello"));
System.out.println(map.get("how are you?"));
}
上述代码编译后的结果是
public static void main(String[] args) {
Map map = new HashMap();
map.put("hello", "你好");
map.put("how are you?", "吃了没?");
System.out.println((String) map.get("hello"));
System.out.println((String) map.get("how are you?"));
}
Java泛型在某些场景是存在缺陷的,例如下面代码是编译不通过的
import java.util.List;
public class GenericTypes {
public static void method(List list) {
System.out.println("invoke method(List list)" );
}
public static void method(List list) {
System.out.println("invoke method(List list)" );
}
}
再看下面这段代码,这段代码增加了一个返回值就可以编译成功,前提是试用JDK1.6的Javac编译器。导致这种情况的出现使因为虚拟机的方法区的实现有关,在Class文件格式中,只要描述符不完全一致,就允许存在。
import java.util.ArrayList;
import java.util.List;
public class GenericTypes {
public static String method(List list) {
System.out.println("invoke method(List list)" );
return "";
}
public static int method(List list) {
System.out.println("invoke method(List list)" );
return 1;
}
public static void main(String[] args) {
method(new ArrayList());
method(new ArrayList());
}
}
Java虚拟机引入了诸如Signature、Local VariableTypeTable等新的属性用于解决伴随泛型而来的的参数类型识别的问题。
下面的代码包含了泛型、自动装箱、自动拆箱、遍历循环与变长参数5种语法糖。
public static void main(String[] args) {
List list = Arrays.asList(1, 2, 3, 4);
// 如果在JDK 1.7中,还有另外一颗语法糖 ,
// 能让上面这句代码进一步简写成List list = [1, 2, 3, 4];
int sum = 0;
for (int i : list) {
sum += i;
}
System.out.println(sum);
}
下面这段代码是上面这段代码的编译之后得到
public static void main(String[] args) {
List list = Arrays.asList( new Integer[] {
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3),
Integer.valueOf(4) });
int sum = 0;
for (Iterator localIterator = list.iterator(); localIterator.hasNext(); ) {
int i = ((Integer)localIterator.next()).intValue();
sum += i;
}
System.out.println(sum);
}
public static void main(String[] args) {
if (true) {
System.out.println("block 1");
} else {
System.out.println("block 2");
}
}
上面的代码反编译后会变成
public static void main(String[] args) {
System.out.println("block 1");
}
下面的代码编译器拒绝编译
public static void main(String[] args) {
// 编译器将会提示“Unreachable code”
while (false) {
System.out.println("");
}
}
Java除了上面说的语法糖外,还有内部类、枚举类、断言语句、对枚举和字符串的switch支持(JDK1.7支持)、try语句中定义和关闭资源(JDK1.7支持)等。
实现一个类似checkstyle、FindBug类似的代码检查工具。为了实现这个效果,我们要定一些书写规范:
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;
// 可以用"*"表示支持所有Annotations
@SupportedAnnotationTypes("*")
// 只支持JDK 1.6的Java代码
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class NameCheckProcessor extends AbstractProcessor {
private NameChecker nameChecker;
/**
* 初始化名称检查插件
*/
@Override
public void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
nameChecker = new NameChecker(processingEnv);
}
/**
* 对输入的语法树的各个节点进行进行名称检查
*/
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!roundEnv.processingOver()) {
for (Element element : roundEnv.getRootElements())
nameChecker.checkNames(element);
}
return false;
}
}
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.*;
import javax.lang.model.util.ElementScanner6;
import java.util.EnumSet;
import static javax.lang.model.element.ElementKind.*;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.lang.model.element.Modifier.STATIC;
import static javax.tools.Diagnostic.Kind.WARNING;
/**
* 程序名称规范的编译器插件:
* 如果程序命名不合规范,将会输出一个编译器的WARNING信息
*/
public class NameChecker {
private final Messager messager;
NameCheckScanner nameCheckScanner = new NameCheckScanner();
NameChecker(ProcessingEnvironment processsingEnv) {
this.messager = processsingEnv.getMessager();
}
/**
* 对Java程序命名进行检查,根据《Java语言规范》第三版第6.8节的要求,Java程序命名应当符合下列格式:
*
*
* - 类或接口:符合驼式命名法,首字母大写。
*
- 方法:符合驼式命名法,首字母小写。
*
- 字段:
*
* - 类、实例变量: 符合驼式命名法,首字母小写。
*
- 常量: 要求全部大写。
*
*
*/
public void checkNames(Element element) {
nameCheckScanner.scan(element);
}
/**
* 名称检查器实现类,继承了JDK 1.6中新提供的ElementScanner6
* 将会以Visitor模式访问抽象语法树中的元素
*/
private class NameCheckScanner extends ElementScanner6<Void, Void> {
/**
* 此方法用于检查Java类
*/
@Override
public Void visitType(TypeElement e, Void p) {
scan(e.getTypeParameters(), p);
checkCamelCase(e, true);
super.visitType(e, p);
return null;
}
/**
* 检查方法命名是否合法
*/
@Override
public Void visitExecutable(ExecutableElement e, Void p) {
if (e.getKind() == METHOD) {
Name name = e.getSimpleName();
if (name.contentEquals(e.getEnclosingElement().getSimpleName()))
messager.printMessage(WARNING, "一个普通方法 “" + name + "”不应当与类名重复,避免与构造函数产生混淆", e);
checkCamelCase(e, false);
}
super.visitExecutable(e, p);
return null;
}
/**
* 检查变量命名是否合法
*/
@Override
public Void visitVariable(VariableElement e, Void p) {
// 如果这个Variable是枚举或常量,则按大写命名检查,否则按照驼式命名法规则检查
if (e.getKind() == ENUM_CONSTANT || e.getConstantValue() != null || heuristicallyConstant(e))
checkAllCaps(e);
else
checkCamelCase(e, false);
return null;
}
/**
* 判断一个变量是否是常量
*/
private boolean heuristicallyConstant(VariableElement e) {
if (e.getEnclosingElement().getKind() == INTERFACE)
return true;
else if (e.getKind() == FIELD && e.getModifiers().containsAll(EnumSet.of(PUBLIC, STATIC, FINAL)))
return true;
else {
return false;
}
}
/**
* 检查传入的Element是否符合驼式命名法,如果不符合,则输出警告信息
*/
private void checkCamelCase(Element e, boolean initialCaps) {
String name = e.getSimpleName().toString();
boolean previousUpper = false;
boolean conventional = true;
int firstCodePoint = name.codePointAt(0);
if (Character.isUpperCase(firstCodePoint)) {
previousUpper = true;
if (!initialCaps) {
messager.printMessage(WARNING, "名称“" + name + "”应当以小写字母开头", e);
return;
}
} else if (Character.isLowerCase(firstCodePoint)) {
if (initialCaps) {
messager.printMessage(WARNING, "名称“" + name + "”应当以大写字母开头", e);
return;
}
} else
conventional = false;
if (conventional) {
int cp = firstCodePoint;
for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {
cp = name.codePointAt(i);
if (Character.isUpperCase(cp)) {
if (previousUpper) {
conventional = false;
break;
}
previousUpper = true;
} else
previousUpper = false;
}
}
if (!conventional)
messager.printMessage(WARNING, "名称“" + name + "”应当符合驼式命名法(Camel Case Names)", e);
}
/**
* 大写命名检查,要求第一个字母必须是大写的英文字母,其余部分可以是下划线或大写字母
*/
private void checkAllCaps(Element e) {
String name = e.getSimpleName().toString();
boolean conventional = true;
int firstCodePoint = name.codePointAt(0);
if (!Character.isUpperCase(firstCodePoint))
conventional = false;
else {
boolean previousUnderscore = false;
int cp = firstCodePoint;
for (int i = Character.charCount(cp); i < name.length(); i += Character.charCount(cp)) {
cp = name.codePointAt(i);
if (cp == (int) '_') {
if (previousUnderscore) {
conventional = false;
break;
}
previousUnderscore = true;
} else {
previousUnderscore = false;
if (!Character.isUpperCase(cp) && !Character.isDigit(cp)) {
conventional = false;
break;
}
}
}
}
if (!conventional)
messager.printMessage(WARNING, "常量“" + name + "”应当全部以大写字母或下划线命名,并且以字母开头", e);
}
}
}
最后在提供一个较差的代码编码规范代码:
public class BADLY_NAMED_CODE {
enum colors {
red, blue, green;
}
static final int _FORTY_TWO = 42;
public static int NOT_A_CONSTANT = _FORTY_TWO;
protected void BADLY_NAMED_CODE() {
return;
}
public void NOTcamelCASEmethodNAME() {
return;
}
}
先用javac编译NameChecker.java和NameCheckProcessor.java;然后javac -processor NameCheckProcessor BADLY_NAMED_CODE.java 命令编译这段示例代码。
Hibernate Validator Annotation Processor、Project Lombok等。