java程序员必须掌握的5个注解,冲刺7天拿下Offer

public abstract class Foo {    public int doSomething() {        return 1;    }    public abstract int doSomethingElse();}public class Bar extends Foo {    @Override    public int doSomething() {        return 10;    }    @Override    public int doSomethingElse() {        return 20;    }}Foo bar = new Bar();System.out.println(bar.doSomething());         // 10System.out.println(bar.doSomethingElse());     // 20

@Override注解不仅不限于超类中的具体或抽象方法,而且还可用于确保接口的方法也被覆盖(从JDK 6开始):

public interface Foo {    public int doSomething();}public class Bar implements Foo {    @Override    public int doSomething() {        return 10;    }}Foo bar = new Bar();System.out.println(bar.doSomething());    // 10

通常,覆盖非final类方法、抽象超类方法或接口方法的任何方法都可以使用@Override进行注解。有关有效覆盖的更多信息,请参阅《Overriding and Hiding》文档 以及《Java Language Specification (JLS)》的第9.6.4.4章节。

@FunctionalInterface


随着JDK 8中lambda表达式的引入,函数式接口在Java中变得越来越流行。这些特殊类型的接口可以用lambda表达式、方法引用或构造函数引用代替。根据@FunctionalInterface文档,函数式接口的定义如下:

一个函数式接口只有一个抽象方法。由于默认方法有一个实现,所以它们不是抽象的。

例如,以下接口被视为函数式接口:

public interface Foo {    public int doSomething();}public interface Bar {    public int doSomething();    public default int doSomethingElse() {        return 1;    }}

因此,下面的每一个都可以用lambda表达式代替,如下所示:

public class FunctionalConsumer {    public void consumeFoo(Foo foo) {        System.out.println(foo.doSomething());    }    public void consumeBar(Bar bar) {        System.out.println(bar.doSomething());    }}FunctionalConsumer consumer = new FunctionalConsumer();consumer.consumeFoo(() -> 10);    // 10consumer.consumeBar(() -> 20);    // 20

重点要注意的是,抽象类,即使它们只包含一个抽象方法,也不是函数式接口。更多信息,请参阅首席Java语言架构师Brian Goetz编写的《Allow lambdas to implement abstract classes》。与@Override注解类似,Java编译器提供了@FunctionalInterface注解以确保接口确实是函数式接口。例如,我们可以将此注解添加到上面创建的接口中:

@FunctionalInterfacepublic interface Foo {    public int doSomething();}@FunctionalInterfacepublic interface Bar {    public int doSomething();    public default int doSomethingElse() {        return 1;    }}

如果我们错误地将接口定义为非函数接口并用@FunctionalInterface注解了错误的接口,则Java编译器会发出错误。例如,我们可以定义以下带注解的非函数式接口:

@FunctionalInterfacepublic interface Foo {    public int doSomething();    public int doSomethingElse();}

如果我们试图编译这个接口,则会收到以下错误:

$ javac Foo.javaFoo.java:1: error: Unexpected @FunctionalInterface annotation@FunctionalInterface^  Foo is not a functional interface    multiple non-overriding abstract methods found in interface Foo1 error

使用这个注解,我们可以确保我们不会错误地创建原本打算用作函数式接口的非函数式接口。需要注意的是,即使在@FunctionalInterface注解不存在的情况下,接口也可以用作函数式接口(可以替代为lambdas,方法引用和构造函数引用),正如我们前面的示例中所见的那样。这类似于@Override注解,即一个方法是可以被覆盖的,即使它不包含@Override注解。在这两种情况下,注解都是允许编译器执行期望意图的可选技术。

有关@FunctionalInterface注解的更多信息,请参阅@FunctionalInterface文档和《 需要zi料+ 绿色徽【vip1024b】

JLS》的第4.6.4.9章节。点击这里阅读 Java 10 新特性实战教程。

@SuppressWarnings


警告是所有编译器的重要组成部分,为开发人员提供的反馈——可能危险的行为或在未来的编译器版本中可能会出现的错误。例如,在Java中使用泛型类型而没有其关联的正式泛型参数(称为原始类型)会导致警告,就像使用不推荐使用的代码一样(请参阅下面的@Deprecated部分)。虽然这些警告很重要,但它们可能并不总是适用甚至并不总是正确的。例如,可能会有对不安全的类型转换发生警告的情况,但是基于使用它的上下文,我们可以保证它是安全的。

为了忽略某些上下文中的特定警告,JDK 5中引入了@SuppressWarnings注解。此注解接受一个或多个字符串参数——描述要忽略的警告名称。虽然这些警告的名称通常在编译器实现之间有所不同,但有3种警告在Java语言中是标准化的(因此在所有Java编译器实现中都很常见):

  • unchecked:表示类型转换未经检查的警告(编译器无法保证类型转换是安全的),导致发生的可能原因有访问原始类型的成员(参见《JLS》4.8章节)、窄参考转换或不安全的向下转换(参见《JLS》5.1.6章节)、未经检查的类型转换(参见《JLS》5.1.9章节)、使用带有可变参数的泛型参数(参见《JLS》8.4.1章节和下面的@SafeVarargs部分)、使用无效的协变返回类型(参见《JLS》8.4.8.3章节)、不确定的参数评估(参见《JLS》15.12.4.2章节),未经检查的方法引用类型的转换(参见《JLS》15.13.2章节)、或未经检查的lambda类型的对话(参见《JLS》15.27.3章节)。

  • deprecation:表示使用了已弃用的方法、类、类型等的警告(参见《JLS》9.6.4.6章节和下面的@Deprecated部分)。

  • removal:表示使用了最终废弃的方法、类、类型等的警告(参见《JLS》9.6.4.6章节和下面的@Deprecated部分)。

为了忽略特定的警告,可以将@SuppressedWarning注解与抑制警告(以字符串数组的形式提供)的一个或多个名字添加到发生警告的上下文中:

public class Foo {    public void doSomething(@SuppressWarnings(“rawtypes”) List myList) {        // Do something with myList    }}

@SuppressWarnings注解可用于以下任何一种情况:

  • 类型

  • 方法

  • 参数

  • 构造函数

  • 局部变量

  • 模块

一般来说,@SuppressWarnings注解应该应用于最直接的警告范围。例如,如果方法中的局部变量应忽略警告,则应将@SuppressWarnings注解应用于局部变量,而不是包含局部变量的方法或类:

public class Foo {    public void doSomething() {        @SuppressWarnings(“rawtypes”)        List myList = new ArrayList();        // Do something with myList    }}

@SafeVarargs


可变参数在Java中是一种很有用的技术手段,但在与泛型参数一起使用时,它们也可能会导致一些严重的问题。由于泛型在Java中是非特定的,所以具有泛型类型的变量的实际(实现)类型不能在运行时被断定。由于无法做出此判断,因此变量可能会存储非其实际类型的引用到类型,如以下代码片段所示(摘自《Java Generics FAQs》):

List ln = new ArrayList();ln.add(1);List ls = ln;                 // unchecked warning String s = ls.get(0);                 // ClassCastException

在将ln分配给ls后,堆中存在变量ls,该变量具有List的类型,但存储引用到实际为List类型的值。这个无效的引用被称为堆污染。由于直到运行时才能确定此错误,因此它会在编译时显示为警告,并在运行时出现ClassCastException。当泛型参数与可变参数组合时,可能会加剧此问题:

public class Foo {    public void doSomething(T… args) {        // …    }}

在这种情况下,Java编译器会在调用站点内部创建一个数组来存储可变数量的参数,但是T的类型并未实现,因此在运行时会丢失。实质上,到doSomething的参数实际上是Object[]类型。如果依赖T的运行时类型,那么这会导致严重的问题,如下面的代码片段所示:

public class Foo {    public void doSomething(T… args) {        Object[] objects = args;        String string = (String) objects[0];    }}Foo foo = new Foo();foo.doSomething(1, 2);

如果执行此代码片段,那么将导致ClassCastException,因为在调用站点传递的第一个Number参数不能转换为String(类似于独立堆污染示例中抛出的ClassCastException)。通常,可能会出现以下情况:编译器没有足够的信息来正确确定通用可变参数的确切类型,这会导致堆污染,这种污染可以通过允许内部可变参数数组从方法中转义来传播,如下面摘自《Effective Java》第3版 pp.147的例子:

public static T[] toArray(T… args) {    return args;}

在某些情况下,我们知道方法实际上是类型安全的,不会造成堆污染。如果可以在保证的情况下做出这个决定,那么我们可以使用@SafeVarargs注解来注解该方法,从而抑制与可能的堆污染相关的警告。但是,这引出了一个问题:什么时候通用可变参数方法会被认为是类型安全的?Josh Bloch在《Effective Java》第3版第147页的基础上提供了一个完善的解决方案——基于方法与内部创建的用于存储其可变参数的数组的交互:

如果方法没有存储任何东西到数组(这会覆盖参数)且不允许对数组的引用进行转义(这会使得不受信任的代码可以访问数组),那么它是安全的。换句话说,如果可变参数数组仅用于从调用者向方法传递可变数量的参数——毕竟,这是可变参数的目的——那么该方法是安全的。

因此,如果我们创建了以下方法(来自pp.149同上),那么我们可以用@SafeVarags注解来合理地注解我们的方法:

@SafeVarargsstatic List flatten(List<? extends T>… lists) {    List result = new ArrayList<>();    for (List<? extends T> list : lists) {        result.addAll(list);    }    return result;}

读者福利

秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer

更多笔记分享

秋招我借这份PDF的复习思路,收获美团,小米,京东等Java岗offer

List flatten(List<? extends T>… lists) {    List result = new ArrayList<>();    for (List<? extends T> list : lists) {        result.addAll(list);    }    return result;}

读者福利

[外链图片转存中…(img-wFCMpevW-1710357216963)]

更多笔记分享

[外链图片转存中…(img-RbKK5Ada-1710357216964)]

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值