@Override
覆盖方法的实现或为抽象方法提供实现的能力是任何面向对象(OO)语言的核心。由于Java是OO语言,具有许多常见的面向对象的抽象机制,所以在非终极超类定义的非最终方法或接口中的任何方法(接口方法不能是最终的)都可以被子类覆盖。虽然开始时覆盖方法看起来很简单,但是如果执行不正确,则可能会引入许多微小的bug。例如,用覆盖类类型的单个参数覆盖Object#equals方法就是一种常见的错误:
public class Foo {
public boolean equals(Foo foo) {
// Check if the supplied object is equal to this object
}
}
由于所有类都隐式地从Object类继承,Foo类的目的是覆盖Object#equals方法,因此Foo可被测试是否与Java中的任何其他对象相等。虽然我们的意图是正确的,但我们的实现则并非如此。实际上,我们的实现根本不覆盖Object#equals方法。相反,我们提供了方法的重载:我们不是替换Object类提供的equals方法的实现,而是提供第二个方法来专门接受Foo对象,而不是Object对象。我们的错误可以用简单实现来举例说明,该实现对所有的相等检查都返回true,但当提供的对象被视为Object(Java将执行的操作,例如在Java Collections Framework即JCF中)时,就永远不会调用它:
public class Foo {
public boolean equals(Foo foo) {
return true;
}
}
Object foo = new Foo();
Object identicalFoo = new Foo();
System.out.println(foo.equals(identicalFoo)); // false
这是一个非常微妙但常见的错误,可以被编译器捕获。我们的意图是覆盖Object#equals方法,但因为我们指定了一个类型为Foo而不是Object类型的参数,所以我们实际上提供了重载的Object#equals方法,而不是覆盖它。为了捕获这种错误,我们引入@Override注解,它指示编译器检查覆盖实际有没有执行。如果没有执行有效的覆盖,则会抛出错误。因此,我们可以更新Foo类,如下所示:
public class Foo {
@Override
public boolean equals(Foo foo) {
return true;
}
}
如果我们尝试编译这个类,我们现在收到以下错误:
$ javac Foo.java
Foo.java:3: error: method does not override or implement a method from a supertype
@Override
^
1 error
实质上,我们已经将我们已经覆盖方法的这一隐含的假设转变为由编译器进行的显性验证。如果我们的意图被错误地实现,那么Java编译器会发出一个错误——不允许我们不正确实现的代码被成功编译。通常,如果以下任一条件不满足,则Java编译器将针对使用@Override注解的方法发出错误(引用自Override注解文档):
该方法确实会覆盖或实现在超类中声明的方法。
该方法的签名与在Object中声明的任何公共方法(即equals或hashCode方法)的签名覆盖等价(override-equivalent)。
因此,我们也可以使用此注解来确保子类方法实际上也覆盖超类中的非最终具体方法或抽象方法:
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()); // 10
System.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); // 10
consumer.consumeBar(() -> 20); // 20
重点要注意的是,抽象类,即使它们只包含一个抽象方法,也不是函数式接口。更多信息,请参阅首席Java语言架构师Brian Goetz编写的《Allow lambdas to implement abstract classes》。与@Override注解类似,Java编译器提供了如果有想学的朋友或者想转行的朋友可以进IT交流q裙哦 这里有大神 也有小白 也有刚刚跟你一样经历过 现在已经做上IT工作的前辈 大家相互学习 相互交流 《49 **6零三吧 649》 一定要 进裙备注:亚索 要不管理员会不加你的。@FunctionalInterface注解以确保接口确实是函数式接口。例如,我们可以将此注解添加到上面创建的接口中:
@FunctionalInterface
public interface Foo {
public int doSomething();
}
@FunctionalInterface
public interface Bar {
public int doSomething();
public default int doSomethingElse() {
return 1;
}
}
如果我们错误地将接口定义为非函数接口并用@FunctionalInterface注解了错误的接口,则Java编译器会发出错误。例如,我们可以定义以下带注解的非函数式接口:
@FunctionalInterface
public interface Foo {
public int doSomething();
public int doSomethingElse();
}
如果我们试图编译这个接口,则会收到以下错误:
$ javac Foo.java
Foo.java:1: error: Unexpected @FunctionalInterface annotation
@FunctionalInterface
^
Foo is not a functional interface
multiple non-overriding abstract methods found in interface Foo
1 error
使用这个注解,我们可以确保我们不会错误地创建原本打算用作函数式接口的非函数式接口。需要注意的是,即使在@FunctionalInterface注解不存在的情况下,接口也可以用作函数式接口(可以替代为lambdas,方法引用和构造函数引用),正如我们前面的示例中所见的那样。这类似于@Override注解,即一个方法是可以被覆盖的,即使它不包含@Override注解。在这两种情况下,注解都是允许编译器执行期望意图的可选技术。
有关@FunctionalInterface注解的更多信息,请参阅@FunctionalInterface文档和《JLS》的第4.6.4.9章节。
@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
}
}
人打赏
2人 点赞
主帖获得的天涯分:0
举报 |
楼主
|
楼主发言:1次 发图:0张 | 添加到话题 |