本文将介绍java 8的第二个特性:类型注解。

注解大家都知道,从java5开始加入这一特性,发展到现在已然是遍地开花,在很多框架中得到了广泛的使用,用来简化程序中的配置。那充满争议的类型注解究竟是什么?复杂还是便捷?

什么是类型注解

在java 8之前,注解只能是在声明的地方所使用,比如类,方法,属性;java 8里面,注解可以应用在任何地方,比如:

  • 创建类实例

new @Interned MyObject();

  • 类型映射

myString = (@NonNull String) str;

  • implements 语句中

class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }

  • throw exception声明

void monitorTemperature() throws @Critical TemperatureException { ... }

需要注意的是,类型注解只是语法而不是语义,并不会影响java的编译时间,加载时间,以及运行时间,也就是说,编译成class文件的时候并不包含类型注解。

类型注解的作用

先看看下面代码

?
1
2
3
Collections.emptyList().add("One");
inti=Integer.parseInt("hello");
System.console().readLine();

上面的代码编译是通过的,但运行是会分别报UnsupportedOperationException; NumberFormatException;NullPointerException异常,这些都是runtime error;

类型注解被用来支持在Java的程序中做强类型检查。配合插件式的check framework,可以在编译的时候检测出runtime error,以提高代码质量。这就是类型注解的作用了。

check framework

check framework是第三方工具,配合Java的类型注解效果就是1+1>2。它可以嵌入到javac编译器里面,可以配合ant和maven使用,也可以作为eclipse插件。地址是http://types.cs.washington.edu/checker-framework/。

check framework可以找到类型注解出现的地方并检查,举个简单的例子:

?
1
2
3
4
5
6
importcheckers.nullness.quals.*;
publicclass

GetStarted {
    voidsample()
{
        @NonNullObject
ref =
newObject();
    }
}

使用javac编译上面的类

?
1
javac
-processor checkers.nullness.NullnessChecker GetStarted.java

编译是通过,但如果修改成

?
1
@NonNullObject
ref =
null;

再次编译,则出现

?
1
2
3
4
5
6
GetStarted.java:5:
incompatible types.
found  
:
@Nullable<nulltype>
required:@NonNullObject
        @NonNullObject
ref =
null;
                              ^
1error

如果你不想使用类型注解检测出来错误,则不需要processor,直接javac GetStarted.java是可以编译通过的,这是在java
8 with Type Annotation Support
版本里面可以,但java 5,6,7版本都不行,因为javac编译器不知道@NonNull是什么东西,但check framework 有个向下兼容的解决方案,就是将类型注解nonnull用/**/注释起来

,比如上面例子修改为

?
1
2
3
4
5
6
importcheckers.nullness.quals.*;
publicclass

GetStarted {
    voidsample()
{
        /*@NonNull*/Object
ref =
null;
    }
}

这样javac编译器就会忽略掉注释块,但用check framework里面的javac编译器同样能够检测出nonnull错误。

通过类型注解+check framework我们可以看到,现在runtime error可以在编译时候就能找到。

关于JSR 308

JSR 308想要解决在Java 1.5注解中出现的两个问题:

  • 在句法上对注解的限制:只能把注解写在声明的地方
  • 类型系统在语义上的限制:类型系统还做不到预防所有的bug

JSR 308 通过如下方法解决上述两个问题:

  • 对Java语言的句法进行扩充,允许注解出现在更多的位置上。包括:方法接收器(method receivers,译注:例public int size() @Readonly { ... }),泛型参数,数组,类型转换,类型测试,对象创建,类型参数绑定,类继承和throws子句。其实就是类型注解,现在是java
    8的一个特性
  • 通过引入可插拔的类型系统(pluggable type systems)能够创建功能更强大的注解处理器。类型检查器对带有类型限定注解的源码进行分析,一旦发现不匹配等错误之处就会产生警告信息。其实就是check framework

对JSR308,有人反对,觉得更复杂更静态了,比如 

@NotEmpty List<@NonNull String> strings = new ArrayList<@NonNull String>()> 

换成动态语言为 

var strings = ["one", "two"]; 



有人赞成,说到底,代码才是“最根本”的文档。代码中包含的注解清楚表明了代码编写者的意图。当没有及时更新或者有遗漏的时候,恰恰是注解中包含的意图信息,最容易在其他文档中被丢失。而且将运行时的错误转到编译阶段,不但可以加速开发进程,还可以节省测试时检查bug的时间。

总结

并不是人人都喜欢这个特性,特别是动态语言比较流行的今天,所幸,java 8并不强求大家使用这个特性,反对的人可以不使用这一特性,而对代码质量有些要求比较高的人或公司可以采用JSR 308,毕竟代码才是“最基本”的文档,这句话我是赞同的。虽然代码会增多,但可以使你的代码更具有表达意义。对这个特性有何看法,大家各抒己见。

重复注解

前面介绍了:
lambda表达式和默认方法 (JEP 126)
批量数据操作(JEP 107)
类型注解(JEP 104) 

注:JEP=JDK Enhancement-Proposal (JDK 增强建议 ),每个JEP即一个新特性。

在java 8里面,注解一共有2个改进,一个是类型注解,在上篇已经介绍了,本篇将介绍另外一个注解的改进:重复注解(JEP 120)。

什么是重复注解

允许在同一申明类型(类,属性,或方法)的多次使用同一个注解

一个简单的例子

java 8之前也有重复使用注解的解决方案,但可读性不是很好,比如下面的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public

@interface

Authority {
     String
role();
}
 
public

@interface

Authorities {
    Authority[]
value();
}
 
public

class

RepeatAnnotationUseOldVersion {
     
    @Authorities({@Authority(role="Admin"),@Authority(role="Manager")})
    public

void

doSomeThing(){
    }
}

由另一个注解来存储重复注解,在使用时候,用存储注解Authorities来扩展重复注解,我们再来看看java 8里面的做法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Repeatable(Authorities.class)
public

@interface

Authority {
     String
role();
}
 
public

@interface

Authorities {
    Authority[]
value();
}
 
public

class

RepeatAnnotationUseNewVersion {
    @Authority(role="Admin")
    @Authority(role="Manager")
    public

void

doSomeThing(){ }
}

不同的地方是,创建重复注解Authority时,加上@Repeatable,指向存储注解Authorities,在使用时候,直接可以重复使用Authority注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点

总结

JEP120没有太多内容,是一个小特性,仅仅是为了提高代码可读性。这次java 8对注解做了2个方面的改进(JEP 104,JEP120),相信注解会比以前使用得更加频繁了。

from :  http://my.oschina.net/benhaile/blog/179642

Java 8新特性探究(二)类型注解和重复注解的更多相关文章

  1. [转帖]Java 8新特性探究(九)跟OOM:Permgen说再见吧

    Java 8新特性探究(九)跟OOM:Permgen说再见吧 https://my.oschina.net/benhaile/blog/214159 need study 很多开发者都在其系统中见过“ ...

  2. Java 8新特性探究(八)精简的JRE详解

    http://www.importnew.com/14926.html     首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 Java小组 工具资源 - 导航条 - 首页 所有文章 资讯 ...

  3. [转帖]Java 8新特性探究(八)精简的JRE详解

    Java 8新特性探究(八)精简的JRE详解 https://my.oschina.net/benhaile/blog/211804 精简版的api   撸了今年阿里.网易和美团的面试,我有一个重要发 ...

  4. [转帖]Java 8新特性探究 前言

    Java 8新特性探究 前言 https://my.oschina.net/benhaile/blog/174136 讲下java的历史 感觉挺好的. 评论 17 jdk8java8javase新特性 ...

  5. Java 8新特性探究(二)深入解析默认方法

    什么是默认方法,为什么要有默认方法 简单说,就是接口可以有实现方法,而且不需要实现类去实现其方法.只需在方法名前面加个default关键字即可. 为什么要有这个特性?首先,之前的接口是个双刃剑,好处是 ...

  6. Java 8新特性探究(三)泛型的目标类型推断

    简单理解泛型 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.通俗点将就是"类型的变量".这种类型变量可以用在类.接口和方法 ...

  7. Java 8新特性探究(九)跟OOM:Permgen说再见吧

    PermGen space简单介绍 元空间(MetaSpace)一种新的内存空间诞生 PermGen 空间的状况 Metaspace 内存分配模型 Metaspace 容量 Metaspace 垃圾回 ...

  8. Java 8新特性(二):Stream API

    本篇文章继续介绍Java 8的另一个新特性--Stream API.新增的Stream API与InputStream和OutputStream是完全不同的概念,Stream API是对Java中集合 ...

  9. Java 8新特性探究(五)Base64详解

    BASE64 编码是一种常用的字符编码,在很多地方都会用到.但base64不是安全领域下的加密解密算法.能起到安全作用的效果很差,而且很容易破解,他核心作用应该是传输数据的正确性,有些网关或系统只能使 ...

随机推荐

  1. chrome下positon:fixed无效或抖动的解决办法

    先来看一下我们要实现的效果 我想这种效果大家都有实现过,或者说吸顶的效果和这差不多 页面结构 js代码如下 /*吸顶*/ var $child = $("#child_3"); v ...

  2. 我与android的缘分

    android的开始 本人是一名大三的学生,大一大二主要学习的是php后台开发,在大一的时候做过一些小的网站系统,也参加过一些大学生计算机相关的比赛.这次开始着手于安卓开发,也是一时的兴起.因为跟我们 ...

  3. web缓存之--http缓存机制

    一.web缓存可以分为数据库缓存.代理服务器缓存.浏览器缓存. 其中浏览器缓存又包含很多内容:http缓存.indexDb.cookie.localStorage等.本片只讨论http缓存相关内容. ...

  4. solr服务器搭建

    百度百科定义:Solr是一个独立的企业级搜索应用服务器,它对外提供类似于Web-service的API接口.用户可以通过http请求,向搜索引擎服务器提交一定格式的XML文件,生成索引:也可以通过Ht ...

  5. docker管理工具

    Portainer是Docker的图形化管理工具,提供状态显示面板.应用模板快速部署.容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作).事件日志显示.容器控制台操作.Swarm集群和服 ...

  6. Android查缺补漏(线程篇)-- IntentService的源码浅析

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8975114.html 在Android中有两个比较容易弄混的概念,Servic ...

  7. ACM Ignatius and the Princess I

    公主被BEelzebub feng5166绑架,我们的英雄Ignatius必须拯救我们美丽的公主. 现在他进入feng5166的城堡.城堡是一个大迷宫.为简单起见,( To make the prob ...

  8. MySQL EXTRACT() 函数

    定义和用法 EXTRACT() 函数用于返回日期/时间的单独部分,比如年.月.日.小时.分钟等等. 语法 EXTRACT(unit FROM date) date 参数是合法的日期表达式.unit 参 ...

  9. 根据class显示或隐藏多个div

    引用一下jquery,然后function放head中 function test(){ $(".1").css("display","none&qu ...

  10. 激活第一个CPU

    回到start_kernel,559行,boot_cpu_init函数,跟start_kernel位于同一文件: 494static void __init boot_cpu_init(void) 4 ...