1.1 抽象类和接口的区别
1.1.1. Abstract classes versus interfaces in Java 8
1.1.2. Conceptual Difference:
1.2 Java 8 中的接口默认方法
1.2.1. What is Default Method?
1.2.2. ForEach method compilation error solved using Default Method
1.2.3. Default Method and Multiple Inheritance Ambiguity Problems
1.2.4. Important points about java interface default methods:
1.3 Java接口静态方法
1.3.1. Java Interface Static Method, code example, static method vs default method
1.3.2. Important points about java interface static method:
1.4 Java 功能接口
1.1.1。 Java 8 中的抽象类与接口
Java 8 接口更改包括静态方法和默认方法
接口。在 Java 8 之前,我们只能在
接口。但是从 Java 8 开始,我们可以使用默认方法和
接口中的静态方法。
在引入 Default Method 之后,似乎接口和
抽象类是相同的。但是,它们仍然是不同的概念
在 Java 8 中。
抽象类可以定义构造函数。他们更有条理,
可以有一个与之关联的状态。相反,默认
方法只能在调用其他方面实现
接口方法,不参考特定实现的
状态。因此,两者都用于不同的目的并在两者之间进行选择
真的取决于场景上下文。
1.1.2。概念差异:
抽象类对接口的骨架(即部分)实现有效,但如果没有匹配的接口,则不应存在。
所以,当抽象类被有效地简化为低可见性的接口骨架实现时,默认方法是否也能将其带走?果断:不!实现接口几乎总是需要一些或所有默认方法所缺乏的类构建工具。如果某些接口没有,那显然是一种特殊情况,不应该让你误入歧途。
1.2 Java 8 中的接口默认方法
Java 8 引入了“Default Method”或(Defender 方法)新功能,允许开发人员在不破坏这些接口的现有实现的情况下向接口添加新方法。它提供了灵活性,允许接口定义实现,在具体类无法为该方法提供实现的情况下,该实现将用作默认值。
让我们考虑一个小例子来理解它是如何工作的:
public interface OldInterface {
public void existingMethod();
default public void newDefaultMethod() {
System.out.println("New default method"
+ " is added in interface");
}
}
以下类将在 Java JDK 8 中成功编译,
public class OldInterfaceImpl implements OldInterface {
public void existingMethod() {
// existing implementation is here…
}
}
如果创建 OldInterfaceImpl 的实例:
OldInterfaceImpl obj = new OldInterfaceImpl ();
// print “New default method add in interface”
obj.newDefaultMethod();
默认方法永远不会是最终的,不能同步也不能
覆盖对象的方法。他们总是公开的,这严重
限制了编写简短且可重用的方法的能力。
默认方法可以提供给接口而不影响实现类,因为它包含一个实现。如果接口中的每个添加方法都定义了实现,则不会影响实现类。实现类可以覆盖接口提供的默认实现。
默认方法允许向现有接口添加新功能
不会破坏这些接口的旧实现。
当我们扩展一个包含默认方法的接口时,我们可以执行以下操作,
- 不覆盖默认方法,将继承默认方法。
- 覆盖默认方法,类似于我们在其中覆盖的其他方法
子类。
- 将默认方法重新声明为抽象,这会强制子类
覆盖它。
1.2.2。使用默认方法解决了 ForEach 方法编译错误
对于 Java 8,JDK 集合已被扩展,并且 forEach 方法被添加到整个集合中(与 lambdas 一起工作)。使用常规方式,代码如下所示,
public interface Iterable<T> {
public void forEach(Consumer<? super T> consumer);
}
由于这个结果导致每个实现类都出现编译错误,因此添加了一个带有必需实现的默认方法,以便不应更改现有实现。
下面是带有默认方法的Iterable接口,
public interface Iterable<T> {
public default void forEach(Consumer
<? super T> consumer) {
for (T t : this) {
consumer.accept(t);
}
}
}
同样的机制已被用于在 JDK 接口中添加 Stream 而不会破坏实现类。
1.2.3。默认方法和多重继承歧义问题
由于java类可以实现多个接口,每个接口可以定义具有相同方法签名的默认方法,因此继承的方法可能会相互冲突。
考虑下面的例子,
public interface InterfaceA {
default void defaultMethod(){
System.out.println("Interface A default method");
}
}
public interface InterfaceB {
default void defaultMethod(){
System.out.println("Interface B default method");
}
}
public class Impl implements InterfaceA, InterfaceB {
}
上面的代码会编译失败,出现如下错误,
java: 类 Impl 继承了 defaultMethod() 的不相关默认值
类型 InterfaceA 和 InterfaceB
为了修复这个类,我们需要提供默认的方法实现:
public class Impl implements InterfaceA, InterfaceB {
public void defaultMethod(){
}
}
此外,如果我们想调用任何超级接口提供的默认实现而不是我们自己的实现,我们可以这样做,
public class Impl implements InterfaceA, InterfaceB {
public void defaultMethod(){
// existing code here..
InterfaceA.super.defaultMethod();
}
}
我们可以选择任何默认实现或两者都作为我们新方法的一部分。
1.2.4。关于java接口默认方法的要点:
- Java 接口默认方法将帮助我们扩展接口,而不必担心破坏实现类。
- Java 接口默认方法弥合了接口和抽象类之间的差异。
- Java 8 接口默认方法将帮助我们避免使用实用程序类,例如所有 Collections 类方法都可以在接口本身中提供。
- Java 接口默认方法将帮助我们移除基实现类,我们可以提供默认实现,实现类可以选择覆盖哪一个。
- 在接口中引入默认方法的主要原因之一是增强 Java 8 中的 Collections API 以支持 lambda 表达式。
- 如果层次结构中的任何类具有相同签名的方法,则默认方法变得无关紧要。默认方法不能覆盖 java.lang.Object 中的方法。道理很简单,因为 Object 是所有 java 类的基类。因此,即使我们将 Object 类方法定义为接口中的默认方法,它也将毫无用处,因为将始终使用 Object 类方法。这就是为什么为了避免混淆,我们不能使用覆盖 Object 类方法的默认方法。
- Java 接口默认方法也称为 Defender 方法或虚拟扩展方法。
资源链接:
- When to use: Java 8+ interface default method, vs. abstract method
- Abstract class versus interface in the JDK 8 era
- Interface evolution via virtual extension methods
1.3 Java接口静态方法
1.3.1。 Java 接口静态方法,代码示例,静态方法与默认方法
Java 接口静态方法类似于默认方法,只是我们不能在实现类中覆盖它们。此功能有助于我们避免在实现类中执行不良的情况下出现不希望的结果。让我们用一个简单的例子来研究一下。
public interface MyData {
default void print(String str) {
if (!isNull(str))
System.out.println("MyData Print::" + str);
}
static boolean isNull(String str) {
System.out.println("Interface Null Check");
return str == null ? true : "".equals(str) ? true : false;
}
}
现在让我们看看一个实现类,它有 isNull() 方法,但实现很差。
public class MyDataImpl implements MyData {
public boolean isNull(String str) {
System.out.println("Impl Null Check");
return str == null ? true : false;
}
public static void main(String args[]){
MyDataImpl obj = new MyDataImpl();
obj.print("");
obj.isNull("abc");
}
}
注意 isNull(String str) 是一个简单的类方法,它不会覆盖接口方法。比如我们给isNull()方法加上@Override注解,就会导致编译错误。
现在,当我们运行应用程序时,我们会得到以下输出。
接口空检查
实现空值检查
如果我们将接口方法从静态变为默认,我们将得到以下输出。
实现空值检查
我的数据打印::
实现空值检查
Java 接口静态方法只对接口方法可见,如果我们从 MyDataImpl 类中删除 isNull() 方法,我们将无法将它用于 MyDataImpl 对象。然而,像其他静态方法一样,我们可以使用类名来使用接口静态方法。例如,一个有效的语句将是:
boolean result = MyData.isNull("abc");
1.3.2。关于java接口静态方法的要点:
- Java 接口静态方法是接口的一部分,我们不能将它用于实现类对象。
- Java 接口静态方法非常适合提供实用方法,例如空值检查、集合排序等。
- Java 接口静态方法通过不允许实现类覆盖它们来帮助我们提供安全性。
- 我们不能为Object类方法定义接口静态方法,我们会得到编译器错误“这个静态方法不能从Object隐藏实例方法”。这是因为在 java 中是不允许的,因为 Object 是所有类的基类,我们不能有一个类级别的静态方法和另一个具有相同签名的实例方法。
- 我们可以使用java接口静态方法去除Collections等实用类,并将其所有静态方法移至对应接口,方便查找和使用。
1.4 Java 功能接口
在结束这篇文章之前,我想简要介绍一下函数式接口。只有一个抽象方法的接口称为功能接口。
引入了一个新注解@FunctionalInterface 来将接口标记为功能接口。 @FunctionalInterface 注释是一种避免在功能接口中意外添加抽象方法的工具。使用它是可选的,但很好的做法。
函数式接口是 Java 8 期待已久且备受追捧的特性,因为它使我们能够使用 lambda 表达式来实例化它们。添加了一个带有一堆功能接口的新包 java.util.function 来为 lambda 表达式和方法引用提供目标类型。我们将在以后的文章中研究函数式接口和 lambda 表达式。
资源位置:
- Java 8 Interface Changes – static method, default method