Java中接口的继承与示例
继承是 OOP(面向对象编程)的重要支柱。它是Java中允许一个类继承另一个类的特性(字段和方法)的机制。和类一样,接口可以有方法和变量,但是接口中声明的方法默认是抽象的(只有方法签名,没有主体)。
在本文中,我们将了解如何在接口中使用继承的概念。
接口是一组规范或语句,它们定义了一个类可以做什么,而不指定该类将如何做。接口总是抽象的。一个具体的类必须实现接口中指定的所有抽象方法。 Java不支持多重继承的概念,以避免在不使用虚拟基类的情况下在 C++ 中遇到的菱形问题。但是, Java支持多接口继承,其中一个接口扩展多个超级接口。以下是用于在Java中扩展多个接口的语法:
access_specifier interface subinterfaceName extends superinterface1, superinterface2, …… {
// Body
}
下面是一个在接口中实现多重继承的例子:
Java
// Java program to demonstrate
// the multiple inheritance
// in interface
// Interface to implement the
// addition and subtraction methods
interface Add_Sub {
public void add(double x, double y);
public void subtract(double x, double y);
}
// Interface to implement the
// multiplication and division
interface Mul_Div {
public void multiply(double x, double y);
public void divide(double x, double y);
}
// Calculator interface which extends
// both the above defined interfaces
interface Calculator extends Add_Sub, Mul_Div {
public void printResult(double result);
}
// Calculator class which
// implements the above
// interface
public class MyCalculator implements Calculator {
// Implementing the addition
// method
public void add(double x, double y)
{
double result = x + y;
printResult(result);
}
// Implementing the subtraction
// method
public void subtract(double x, double y)
{
double result = x - y;
printResult(result);
}
// Implementing the multiplication
// method
public void multiply(double x, double y)
{
double result = x * y;
printResult(result);
}
// Implementing the division
// method
public void divide(double x, double y)
{
double result = x / y;
printResult(result);
}
// Implementing a method
// to print the result
public void printResult(double result)
{
System.out.println(
"The result is : " + result);
}
// Driver code
public static void main(String args[])
{
// Creating the object of
// the calculator
MyCalculator c = new MyCalculator();
c.add(5, 10);
c.subtract(35, 15);
c.multiply(6, 9);
c.divide(45, 6);
}
}
Java
// Java program to demonstrate the
// inheritance of the variables
// Defining an interface X
// with some variables
interface X {
int VALUE = 100;
int h = 10;
}
// Defining the interface Y
// with the same variables
// as the interface X
interface Y extends X {
int VALUE = 200;
String h = "Hello World";
int sub = VALUE - X.VALUE;
}
// A class which implements the
// above interface
public class GFG implements Y {
// Printing the values of the
// variables
public static void main(String args[])
{
System.out.println("X.VALUE = " + X.VALUE);
System.out.println("Y.VALUE = " + Y.VALUE);
System.out.println("sub = " + sub);
System.out.println("X.h = " + X.h);
System.out.println("h = " + h);
}
}
Java
// Java program to demonstrate the
// case when the constant variable
// with the same name is defined
// Implementing an interface
// X with a variable A
interface X {
int A = 10;
}
// Implementing an interface
// Y with the same variable
interface Y {
String A = "hi";
}
// Implementing an interface which
// extends both the above interfaces
interface Z extends X, Y {
// body
}
// Creating a class which implements
// the above interface
public class GFG implements Z {
public static void main(String args[])
{
// Shows compile time error if
// the backslash is removed
// System.out.println(Z.A);
System.out.println(X.A);
System.out.println(Y.A);
}
}
The result is : 15.0
The result is : 20.0
The result is : 54.0
The result is : 7.5
超接口-子接口关系:子接口类型引用变量可以分配给超接口类型引用变量。让我们考虑一个示例,其中我们有一个名为animal 的接口,该接口由接口哺乳动物扩展。我们有一个名为 horse 的类实现了动物接口和一个名为 human 的类实现了哺乳动物。当我们将哺乳动物分配给动物时,它编译时没有任何错误,因为哺乳动物也是动物。那是:
// Animal Interface
public interface Animal {
// body
}
// Mammal interface which extends
// the Animal interface
public interface Mammal extends Animal {
// body
}
// Horse which implements the
// Animal
class Horse implements Animal {
// body
}
// Human which implements the
// mammal
class Human implements Mammal {
// body
}
public class GFG {
public static void main(String args[])
{
Animal a = new Horse();
Mammal m = new Human();
// This compiles without any error
// because mammal is also an animal,
// subtype reference variable m
// is assigned to the super type
// reference variable a
a = m;
}
}
多重继承如何影响变量:
将一个接口中定义的变量继承到其他接口中时,有两种可能的情况。他们是:
- 情况一:子接口继承了父接口的所有常量变量。如果子接口声明了一个与继承的常量同名的常量变量,那么新的常量变量将隐藏继承的常量,而不管其类型如何。例如:
Java
// Java program to demonstrate the // inheritance of the variables // Defining an interface X // with some variables interface X { int VALUE = 100; int h = 10; } // Defining the interface Y // with the same variables // as the interface X interface Y extends X { int VALUE = 200; String h = "Hello World"; int sub = VALUE - X.VALUE; } // A class which implements the // above interface public class GFG implements Y { // Printing the values of the // variables public static void main(String args[]) { System.out.println("X.VALUE = " + X.VALUE); System.out.println("Y.VALUE = " + Y.VALUE); System.out.println("sub = " + sub); System.out.println("X.h = " + X.h); System.out.println("h = " + h); } }
输出:X.VALUE = 100 Y.VALUE = 200 sub = 100 X.h = 10 h = Hello World
- 情况2:当子接口扩展具有相同名称的常量变量的接口时, Java给出编译错误,因为编译器无法决定继承哪个常量变量。例如:
Java
// Java program to demonstrate the // case when the constant variable // with the same name is defined // Implementing an interface // X with a variable A interface X { int A = 10; } // Implementing an interface // Y with the same variable interface Y { String A = "hi"; } // Implementing an interface which // extends both the above interfaces interface Z extends X, Y { // body } // Creating a class which implements // the above interface public class GFG implements Z { public static void main(String args[]) { // Shows compile time error if // the backslash is removed // System.out.println(Z.A); System.out.println(X.A); System.out.println(Y.A); } }
输出:10 hi
多重继承如何影响处理冲突的方法和方式:
超接口的所有抽象方法和默认方法都由子接口继承。当子接口扩展多个接口时,就会出现默认默认冲突或抽象默认冲突。这些冲突由以下任一规则处理:
- 用抽象方法覆盖冲突的方法。
- 用默认方法覆盖冲突的方法并提供新的实现。
- 用默认方法覆盖冲突的方法,调用立即超接口的默认方法。
默认-默认冲突:当两个接口使用不同的操作实现相同的默认方法并且当这两个接口都被第三个接口扩展时编译器不知道需要执行哪个任务时,就会出现默认-默认冲突。例如:
// Interface one
interface one {
// A default method
default void
print()
{
System.out.println("one");
}
}
// Interface two
interface two {
// Another default method
// with the same name
default void
print()
{
System.out.println("two");
}
}
我们可以应用上面讨论的规则来解决上述冲突。以下是处理冲突的方法:
- 规则 1:用抽象方法覆盖冲突的方法。例如:
interface four extends one, two { // Implementing another method void print(); }
- 规则 2:用默认方法覆盖冲突的方法并提供新的实现。例如:
interface four extends one, two { // Declare another method and // provide an implementation default void print() { System.out.println("four"); } }
- 规则三:用默认方法覆盖冲突方法,调用直接超接口的默认方法。例如:
interface four extends one, two { // Override the method with a // default method and give the // signature of what to perform default void print() { two.super.print(); System.out.println("-four"); } }
抽象默认冲突:当一个接口实现了默认方法而另一个接口定义了同名的抽象方法时,就会出现抽象默认冲突。在实现这两个接口时,我们需要提供方法的实现,但默认方法不需要实现。即使在这种情况下,编译器也不知道要执行哪个方法。例如:
// Implementing the interface
// one with a default method
interface one {
default void
print()
{
System.out.println("one");
}
}
// Implementing another interface
// with an abstract method of the
// same name
interface three {
void print();
}
我们可以应用上面讨论的规则来解决上述冲突。以下是处理冲突的方法:
- 规则 1:用抽象方法覆盖冲突的方法。例如:
interface five extends one, three { void print(); } }
- 规则 2:用默认方法覆盖冲突的方法并提供新的实现。例如:
interface five extends one, three { default void print() { System.out.println("five"); } }
- 规则三:用默认方法覆盖冲突方法,调用直接超接口的默认方法。例如:
interface five extends one, three { default void print() { one.super.print(); System.out.println("-four"); } }
在接口继承中重写继承的静态方法:
在接口继承中,静态方法在整个执行过程中不会改变,也不会被继承。因此,它们不能被覆盖。但是,如果一个类通过提供具有相同签名的方法来实现多个没有父子关系的接口,并且该类没有覆盖这些方法,则会发生冲突。例如:
// Interface A with an
// abstract method
interface A {
void m();
}
// Interface B which doesn't
// implement the above interface
// and have the same abstract method
interface B {
void m()
{
System.out.println("In B");
}
}
// An Abstract class with the
// normal method M
class abstract C {
public void m()
{
System.out.println("In C");
}
}
public class test extends C
implements A, B {
public static void main(String args[])
{
// Creating an object of test
test t = new test();
// Here, a conflict occurs as to
// which method needs to be called
t.m();
}
}
为了克服这个问题,有三个规则:
- 规则 1:超类的优先级高于接口。这意味着调用了超类方法。
- 规则 2:派生接口比继承层次结构中的超级接口具有更高的优先级。如果 I1 有一个方法 m1() 并且 I2 扩展 I1 并覆盖 m1() 那么 I2 是最具体的版本并且具有更高的优先级。
- 规则 3:类必须根据需要重写方法。例如:
public class test extends C implements A, B { // Overriding the conflicting // method public void m() { System.out.println("test"); } public static void main(String args[]) { test t = new test(); t.m(); } }
总而言之,虽然接口支持多重继承,但需要处理的限制和冲突很少。