
多年来,Java中的接口已经有了很大的发展。 让我们看看它的开发过程中发生了什么变化。
原始界面
与现在的接口相比,Java 1.0中的接口非常简单。 它们只能包含两种类型的元素:常量和公共抽象方法。
常数场
接口可以像常规类一样包含字段,但有一些区别:
- 字段必须初始化。
- 字段被视为public static final
- 不需要明确指定修饰符public,static和final(默认情况下它们是“放下”的)
public interface MyInterface { int MY_CONSTANT = 9; }
即使未明确指定,MY_CONSTANT字段也被视为公共静态最终常量。 您可以添加这些修饰符,但这不是必需的。
抽象方法
接口最重要的元素是其方法。 接口方法也不同于常规类方法:
- 方法没有身体
- 方法实现由实现此接口的类提供。
- 即使没有明确指定,方法也被认为是公共的和抽象的。
- 方法不能是final的,因为Java中不允许abstract和final修饰符的组合
public interface MyInterface { int doSomething(); String doSomethingCompletelyDifferent(); }
套料
Java 1.1引入了可以放置在其他类中的类的概念。 这样的类有两种类型:静态和非静态。 接口还可以包含其他接口和类。
即使未明确指定,此类接口和类也被视为公共和静态的。
public interface MyInterface { class MyClass {
枚举和注释
Java 5中引入了另外两种类型:Enumerations和Annotations。 它们也可以放在接口内部。
public interface MyInterface { enum MyEnum { FOO, BAR; } @interface MyAnnotation {
通用类型
Java 5引入了泛型,泛型类型的概念。 简而言之:泛型允许您使用泛型类型而不是指定特定类型。 因此,您可以编写适用于不同数量类型的代码,而不会牺牲安全性,也无需为每种类型提供单独的实现。
在以Java 5开头的接口中,您可以定义一个通用类型,然后将其用作方法的返回值类型或方法的参数类型。
无论您使用Box接口来存储诸如String,Integer,List,Shoe或任何其他对象,该接口都有效。
interface Box<T> { void insert(T item); } class ShoeBox implements Box<Shoe> { public void insert(Shoe item) {
静态方法
从Java 8开始,您可以在接口中包括静态方法。 这种方法改变了界面为我们工作的方式。 现在,它们的工作方式与Java 8之前的工作方式截然不同。最初,接口中的所有方法都是抽象的。 这意味着该接口仅提供签名,但不提供实现。 该实现留给了实现您的接口的类。
在接口中使用静态方法时,还需要提供方法主体的实现。 要在接口中使用此方法,只需使用static关键字。 默认情况下,静态方法被视为公共方法。
public interface MyInterface {
静态方法继承
与常规静态方法不同,接口中的静态方法不会被继承。 这意味着,如果要调用这样的方法,则必须直接从接口而不是从实现它的类中调用它。
MyInterface.staticMethod();
此行为对于避免多重继承问题非常有用。 假设您有一个实现两个接口的类。 每个接口都有一个具有相同名称和签名的静态方法。 首先应使用哪个?
为什么有用
想象一下,您有一个接口以及与实现此接口的类一起使用的整套帮助程序方法。
传统上,有一种使用伴随类的方法。 除了接口之外,还创建了一个实用程序类,其名称非常相似,其中包含属于该接口的静态方法。
您可以直接在JDK中找到使用此方法的示例:java.util.Collection接口和随附的实用程序类java.util.Collections。
对于接口中的静态方法,此方法不再适用,不再需要并且不建议使用。 现在,您可以将所有内容放在一个地方。
默认方法
默认方法与静态方法相似,因为您还必须为其提供一个主体。 要声明默认方法,只需使用default关键字。
public interface MyInterface { default int doSomething() { return 0; } }
与静态方法不同,默认情况下,方法是由实现接口的类继承的。 重要的是,此类在必要时可以重新定义其行为。
虽然有一个例外。 接口不能具有与Object类的toString,equals和hashCode方法具有相同签名的默认方法。 看一看Brian Goetz的答案,以了解这种解决方案的有效性:
允许默认方法覆盖Object的方法。为什么有用
直接在接口中实现方法的想法似乎并不完全正确。 那么为什么首先引入此功能呢?
接口有一个问题。 一旦将您的API提供给其他人,它将永远被“石化”(不能轻易更改)。
按照传统,Java非常重视向后兼容性。 默认方法提供了一种使用新方法扩展现有接口的方法。 最重要的是,默认方法已经提供了特定的实现。 这意味着实现您的接口的类不需要实现任何新方法。 但是,如果有必要,如果默认方法的实现不再合适,则可以随时覆盖它们。 因此,简而言之,您可以为实现接口的现有类提供新功能,同时保持兼容性。
矛盾冲突
假设我们有一个实现两个接口的类。 这些接口具有具有相同名称和签名的默认方法。
interface A { default int doSomething() { return 0; } } interface B { default int doSomething() { return 42; } } class MyClass implements A, B { }
现在,具有相同签名的相同默认方法将从两个不同的接口继承。 每个接口都有此方法的自己的实现。
那么,我们的班级如何知道要使用两种不同实现中的哪种?
他不会知道。 上面的代码将导致编译错误。 如果需要使其工作,则需要重写类中的冲突方法。
interface A { default int doSomething() { return 0; } } interface B { default int doSomething() { return 42; } } class MyClass implements A, B {
私人方法
随着Java 8的出现以及默认方法和静态方法的引入,接口现在不仅能够包含方法签名,而且还能够包含其实现。 在编写此类实现时,建议将复杂的方法分为更简单的方法。 这样的代码更易于重用,维护和理解。
为此,您将使用专用方法,因为它们可以包含所有不应从外部看到和使用的实现细节。
不幸的是,在Java 8中,接口不能包含私有方法。 这意味着您可以使用:
- 长期,复杂且难以理解的身体技巧。
- 界面中的辅助方法。 这违反了封装原理,并且污染了接口和实现类的公共API。
幸运的是,从
Java 9开始
,您可以在interfaces中使用私有方法 。 它们具有以下功能:
- 私有方法有一个主体,它们不是抽象的
- 它们可以是静态的也可以是非静态的
- 它们没有被实现接口和接口的类所继承
- 他们可以调用其他接口方法
- 私有方法可以调用其他私有,抽象,静态或默认方法
- 私有静态方法只能调用其他静态和私有静态方法
public interface MyInterface { private static int staticMethod() { return 42; } private int nonStaticMethod() { return 0; } }
年代顺序
以下是Java版本更改的时间顺序列表:
Java 1.1
嵌套类
嵌套接口
Java 5
通用类型
封闭转账
嵌套注释
Java 8
默认方法
静态方法
Java 9
私人方法