在Java程序设计语言中,接口不是类,而是对希望符合这个接口的类的一组需求(类似于抽象类,皆为抽象内容,却又区别于抽象类)。由于Java不支持多重继承特性,每个类只能扩展一个类,但可以实现多个接口。实际上,接口可以提供多重继承的大多数好处,同时还能避免多重继承的复杂性和低效性。
2.属性-
接口不是类,具体来说,不能使用 new 运算符实例化一个接口。
InterfaceName aInterface = new InterfaceName() // Error -
尽管不能构造接口的对象,却能声明接口的变量。接口变量必须引用实现implements了该接口的类对象。
class ClassName implements InterfaceName {…}
InterfaceName aClass = new ClassName(); -
如同使用 instanceof 检查一个对象是否属于某个特定类一样,也可以使用 instanceof 检查一个对象是否实现类某个特定的接口。
anObject instanceof InterfaceName -
与建立类的继承层次一样,也可以扩展extends接口层次。允许存在多条接口链,从通用性较高的接口扩展到专用性较高的接口。接口不同于类,可以实现多重继承。
interface InterfaceName1 extends InterfaceName {…}
interface InterfaceName2 extends InterfaceName {…}
interface InterfaceName3 extends InterfaceName1, InterfaceName2 {…}
… … -
虽然在接口中不能包含实例字段,但是可以包含常量。与接口中的方法自动被设置为public abstract一样,接口中的字段总是public static final。(Java语言规范建议不要提供多余的关键字,即无需修饰)
-
静态方法:在Java 8中,允许在接口中定义静态static方法。理论上讲,没有任何理由认为这是不合法的,只是这有违于将接口作为抽象规范的初衷。目前为止,通常的做法都是将静态方法放在伴随类中。在标准库中,你会看到成对出现的接口和实用工具类, 如Collection/Collections 或Path/Paths。
-
私有方法:在Java 9中,接口中的方法可以是private。private方法可以是静态方法或实例方法。由于私有方法只能在接口本身的方法中使用,所以它们的用法很有限,只能作为接口中其他方法的辅助方法。
-
默认方法:在Java 8之前,接口中的方法都是抽象的,不允许有方法体。在此之后可以为接口方法提供一个默认实现,必须用default修饰符标记这样一个方法。默认方法可以调用其他方法。
- 解决默认方法冲突问题:
- 超类优先。如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略。
- 接口冲突。如果一个接口提供了一个默认方法,另一个接口提供了一个同名而且参数类型相同的方法(不管是否为默认方法),必须覆盖这个方法来解决冲突。
- 情形一:会继承接口中多个签名相同、实现不同的默认方法。类必须重写该方法,以解决二义性问题。
class ClassName implements InterfaceName1, InterfaceName2 {
@Override
public String getInterfaceName() { return " InterfaceName1 + InterfaceName2"; }
…
}
interface InterfaceName1 {
default String getInterfaceName() { return " InterfaceName1"; }
…
}
interface InterfaceName2 {
default String getInterfaceName() { return " InterfaceName2"; }
…
} - 情形二:会继承接口中多个签名相同的默认方法与抽象方法。类必须重写该方法,以解决二义性问题。
class ClassName implements InterfaceName1, InterfaceName2 {
@Override
public String getInterfaceName() { return " InterfaceName1 + InterfaceName2"; }
…
}
interface InterfaceName1 {
default String getInterfaceName() { return " InterfaceName1";}
…
}
interface InterfaceName2 {
String getInterfaceName();
…
} - 情况三:存在多个签名相同的(非默认)方法(与Java 8之前情形一样,不存在冲突)。两种选择:①类实现该方法;②不实现该方法,作为抽象类。
①class ClassName implements InterfaceName1, InterfaceName2 {
@Override
public String getInterfaceName() { return " InterfaceName1 + InterfaceName2"; }
…
}
或
②abstract class ClassName implements InterfaceName1, InterfaceName2 {
abstract public String getInterfaceName() // 可有可无
…
}
interface InterfaceName1 {
String getInterfaceName();
…
}
interface InterfaceName2 {
String getInterfaceName();
…
} - 情况四:扩展了超类,同时实现了接口,会继承多个签名相同的方法。此时,子类只会考虑超类的具体方法,接口的默认方法都会被忽略。
class ClassName extends SuperClassName implements InterfaceName {
//getInterfaceName()方法不存在问题,只会继承超类该方法。
…
}
class SuperClassName {
public String getInterfaceName() { return " null"; }
…
}
interface InterfaceName {
default String getInterfaceName() { return " InterfaceName";}
…
}
- 对于有且仅有一个抽象方法的接口,称为函数式接口(functional interface,其注解为@FunctionalInterface)
- 为什么函数式接口只有一个抽象方法,接口中的所有方法不应该都是抽象的吗?
通过查看注解@FunctionalInterface的 javac 注释,或许能找到答案:
①由于默认方法和静态方法具有实现,因此它们不再是抽象方法范畴。
②如果接口声明了一个覆盖 java.lang.Object 的公共方法之一的抽象方法,这也不会计入接口的抽象方法计数,因为接口的任何实现都将具有来自 java.lang.Object 或其他地方的实现
在Java 8中新增加的 java.util.function 包内置了四大核心函数式接口(Consumer消费型、Function函数型、Predicate断言型和Supplier供给型)及其派生函数式接口。
4.1 Consumer消费型@FunctionalInterface public interface Consumer4.2 Function函数型{ void accept(T t); }
@FunctionalInterface public interface Function4.3 Predicate断言型{ R apply(T t); }
@FunctionalInterface public interface Predicate4.4 Supplier供给型{ boolean test(T t); }
@FunctionalInterface public interface Supplier{ T get(); }



