[Java Learning Fundamentals] Java's Abstract Classes and Interfaces [Java Learning Fundamentals] Java's Inheritance and Polymorphism

An abstract class

1. Abstract class

  The Java language provides two classes: one is a concrete class; the other is an abstract subclass.

2. Abstract class concept:

  In object-oriented concept, all objects are described by classes, but in turn, not all classes are used to describe objects, if a class does not contain enough information to describe a specific object , such a class is an abstract class. Except that the abstract class cannot instantiate the object, other functions of the class still exist, and the access methods of member variables, member methods and constructors are the same as those of ordinary classes.

  Since abstract classes cannot instantiate objects, abstract classes must be inherited in order to be used. It is also for this reason that the decision to design abstract classes is usually made during the design phase. The parent class contains the common methods of the child class collection, but since the parent class itself is abstract, these methods cannot be used. In Java, abstract classes represent an inheritance relationship. A class can only inherit one abstract class, but a class can implement multiple interfaces.

  When introducing polymorphism in [Java Learning Basics] Inheritance and Polymorphism in Java, I used the example of the geometry class, in which there is an onDraw (drawing) method in the Figure (geometry) class, and Figure has two subclasses Ellipse (ellipse ) and Triangle (triangle), Ellipse and Triangle override the onDraw method.

  As the parent class Figure (geometry), I don't know how many subclasses there are in actual use. Currently, there are ellipses and triangles, so different user needs may have other geometric shapes such as rectangles or circles, and the onDraw method only determines whether Which subclass can only be implemented concretely. The onDraw method in Figure cannot be implemented concretely, so it can only be an abstract method. A class with abstract methods in Java is called "abstract class", Figure is an abstract class, and the onDraw method is an abstract method. As shown in the following figure, Figure is an abstract class in the class diagram, Ellipse and Triangle are the abstract methods onDraw of Figure subclasses to implement Figure.

Tips: The fonts of abstract classes and abstract methods in UML class diagrams are in italics. As shown in the figure above, Figure classes and onDraw methods are in italics.

3. Abstract class declaration and implementation

The purpose of designing abstract methods is to be implemented by subclasses, otherwise abstract methods have no meaning. The sample code for implementing abstract classes is as follows:

1  // Ellipse.java file 
2  package com.Kevin;
 3  
4  // Geometry ellipse 
5  public  class Ellipse extends Figure {
 6  
7      // Drawing geometry method 
8      @Override
 9      public  void onDraw() {
 10          System.out .println("Draw an ellipse..." );
 11      }
 12  }
 13  
14  // Triangle.java file 
15  package com.Kevin;
 16  
17  // Geometry triangle 
18 public  class Triangle extends Figure {
 19  
20      // Drawing geometry method 
21      @Override
 22      public  void onDraw() {
 23          System.out.println("Drawing triangle..." );
 24      }
 25 }

The above code declares two concrete classes Ellipse and Triangle, which implement (override) the abstract method onDraw of the abstract class Figure.

The calling code is as follows:

// HelloWorld.java file 
package com.Kevin;

public class HelloWorld {

    public static void main(String[] args) {

        // The f1 variable is the parent class type, pointing to the subclass instance, polymorphism occurs 
        Figure f1 = new Triangle();
        f1.onDraw();

        // The f2 variable is the parent class type, pointing to the subclass instance, polymorphism occurs 
        Figure f2 = new Ellipse();
        f2.onDraw();
    }
}

Two concrete classes Triangle and Ellipse are instantiated in the above code, and objects f1 and f2 are Figure reference types.

Declaring an abstract method has two consequences:

  • If a class contains abstract methods, then the class must be abstract.
  • Any subclass must override the abstract method of the superclass, or declare itself as abstract.

Tips:  Abstract classes cannot be instantiated, only concrete classes can be instantiated.

4. Abstract class summary provides:

  • Abstract classes cannot be instantiated (a mistake that beginners are prone to make). If they are instantiated, an error will be reported and the compilation will fail. Only non-abstract subclasses of abstract classes can create objects.
  • Abstract classes do not necessarily contain abstract methods, but classes with abstract methods must be abstract classes.
  • The abstract method in the abstract class is only a declaration, not including the method body, that is, the specific implementation of the method is not given, that is, the specific function of the method.
  • Constructors, class methods (methods decorated with static) cannot be declared as abstract methods.
  • A subclass of an abstract class must give concrete implementations of abstract methods in the abstract class, unless the subclass is also an abstract class.

 2. Use the interface

  Interface (English: Interface), in the JAVA programming language, is an abstract type and a collection of abstract methods. Interfaces are usually declared as interfaces. A class inherits the abstract methods of the interface by inheriting the interface.

  An interface is not a class, the way of writing an interface is very similar to a class, but they belong to a different concept. A class describes the properties and methods of an object. An interface contains the methods to be implemented by the class.

  Unless the class implementing the interface is an abstract class, the class defines all the methods in the interface.

  An interface cannot be instantiated, but it can be implemented. A class that implements an interface must implement all the methods described in the interface, otherwise it must be declared as an abstract class. In addition, in Java, interface types can be used to declare a variable, they can be a null pointer, or be bound to an object implemented by this interface.

1. Interface declaration and implementation

在Java中接口的声明使用的关键字是interface,声明接口Figure示例代码如下:

 1 //Figure.java文件
 2 package com.Kevin;
 3 
 4 public interface Figure {                                 
 5     //接口中静态成员变量
 6     String name = "几何图形";//省略public static final    
 7 
 8     // 绘制几何图形方法
 9     void onDraw();            //省略public                
10 }

代码第4行是声明Figure接口,声明接口使用interface关键字,interface前面的修饰符是public或省略。public是公有访问级别,可以在任何地方访问。省略是默认访问级别,只能在当前包中访问。

代码第6行声明接口中的成员变量,在接口中成员变量都静态成员变量,即省略了public static final修饰符。代码第99行是声明抽象方法,即省略了public关键字。

某个类实现接口时,要在声明时使用implements关键字,当实现多个接口之间用逗号(,)分隔。实现接口时要实现接口中声明的所有方法。

实现接口Figure示例代码如下:

 1 //Ellipse.java文件
 2 package com.Kevin.imp;
 3 
 4 import com.Kevin.Figure;
 5 
 6 //几何图形椭圆形
 7 public class Ellipse implements Figure {
 8 
 9     //绘制几何图形方法
10     @Override
11     public void onDraw() {
12         System.out.println("绘制椭圆形...");
13     }
14 }
15 
16 //Triangle.java文件
17 package com.Kevin.imp;
18 
19 import com.Kevin.Figure;
20 
21 //几何图形三角形
22 public class Triangle implements Figure {
23 
24     // 绘制几何图形方法
25     @Override
26     public void onDraw() {
27         System.out.println("绘制三角形...");
28     }
29 }

上述代码声明了两个具体类Ellipse和Triangle,它们实现了接口Figure中的抽象方法onDraw。

调用代码如下:

 1 //HelloWorld.java文件
 2 import com.Kevin.imp.Ellipse;
 3 import com.Kevin.imp.Triangle;
 4 
 5 public class HelloWorld {
 6 
 7     public static void main(String[] args) {
 8 
 9         // f1变量是父类类型,指向子类实例,发生多态
10         Figure f1 = new Triangle();
11         f1.onDraw();
12         System.out.println(f1.name);         
13         System.out.println(Figure.name);     
14 
15         // f2变量是父类类型,指向子类实例,发生多态
16         Figure f2 = new Ellipse();
17         f2.onDraw();
18     }
19 }

上述代码中实例化两个具体类Triangle和Ellipse,对象f1和f2是Figure接口引用类型。接口Figure中声明了成员变量,它是静态成员变量,代码第12行和第13行是访问name静态变量。

Tips: 接口与抽象类一样都不能被实例化。

 2.接口与多继承

  在C++语言中一个类可以继承多个父类,但这会有潜在的风险,如果两个父类在有相同的方法,那么子类将继承哪一个父类方法呢?这就是C++多继承所导致的冲突问题。

  在Java中只允许继承一个类,但可实现多个接口。通过实现多个接口方式满足多继承的设计需求。如果多个接口中即便有相同方法,它们也都是抽象的,子类实现它们不会有冲突。如下图所示是多继承类图,其中的有两个接口InterfaceA和InterfaceB,从类图中可以见两个接口中都有一个相同的方法void methodB()。AB实现了这两个接口,继承了Object父类。

        

接口InterfaceA和InterfaceB代码如下:

 1 //InterfaceA.java文件
 2 package com.Kevin;
 3 
 4 public interface InterfaceA {
 5 
 6     void methodA();
 7 
 8     void methodB();
 9 }
10 
11 //InterfaceB.java文件
12 package com.Kevin;
13 
14 public interface InterfaceB {
15 
16     void methodB();
17 
18     void methodC();
19 }

从代码中可见两个接口都有两个方法,其中方法methodB()完全相同。

实现接口InterfaceA和InterfaceB的AB类代码如下:

 1 //AB.java文件
 2 package com.Kevin.imp;
 3 
 4 import com.Kevin.InterfaceA;
 5 import com.Kevin.InterfaceB;
 6 
 7 public class AB extends Object implements InterfaceA, InterfaceB {    
 8 
 9     @Override
10     public void methodC() {
11     }
12 
13     @Override
14     public void methodA() {
15     }
16 
17     @Override
18     public void methodB() {            
19     }
20 }

在AB类中的代码第18行实现methodB()方法。注意在AB类声明时,实现两个接口,接口之间使用逗号(,)分隔,见代码第7行。

3.接口继承

  Java语言中允许接口和接口之间继承。由于接口中的方法都是抽象方法,所以继承之后也不需要做什么,因此接口之间的继承要比类之间的继承简单的多。如下图所示,其中InterfaceB继承了InterfaceA,在InterfaceB中还覆盖了InterfaceA中的methodB()方法。ABC是InterfaceB接口的实现类,从下图中可见ABC需要实现InterfaceA和InterfaceB接口中的所有方法。

      

接口InterfaceA和InterfaceB代码如下:

 1 //InterfaceA.java文件
 2 package com.Kevin;
 3 
 4 public interface InterfaceA {
 5 
 6     void methodA();
 7 
 8     void methodB();
 9 }
10 
11 //InterfaceB.java文件
12 package com.Kevin;
13 
14 public interface InterfaceB extends InterfaceA {
15 
16     @Override
17     void methodB();
18 
19     void methodC();
20 }

InterfaceB继承了InterfaceA,声明时也使用extends关键字。InterfaceB 中的methodB()覆盖了InterfaceA,事实上在接口中覆盖方法,并没有实际意义,因为它们都是抽象的,都是留给子类实现的。

实现接口InterfaceB的ABC类代码如下:

 1 //ABC.java文件
 2 package com.Kevin.imp;
 3 
 4 import com.Kevin.InterfaceB;
 5 
 6 public class ABC implements InterfaceB {
 7 
 8     @Override
 9     public void methodA() {
10     }
11 
12     @Override
13     public void methodB() {
14     }
15 
16     @Override
17     public void methodC() {
18     }
19 }

ABC类实现了接口InterfaceB,事实上是实现InterfaceA和InterfaceB中所有方法,相当于同时实现InterfaceA和InterfaceB接口。

4.标记接口

  最常用的继承接口是没有包含任何方法的接口。

  标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。

  标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。

例如:java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:

package java.util;
public interface EventListener
{}

没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:

  • 建立一个公共的父接口:

    正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。

  • 向一个类添加数据类型:

    这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。

 三、Java8新特性(默认方法和静态方法)

在Java 8之前,尽管Java语言中接口已经非常优秀了,但相比其他面向对象的语言而言Java接口存在如下不足之处:

  1. 不能可选实现方法,接口的方法全部是抽象的,实现接口时必须全部实现接口中方法,哪怕是有些方法并不需要,也必须实现。

  2. 没有静态方法。

针对这些问题,Java 8在接口中提供了声明默认方法和静态方法的能力。接口示例代码如下:

 1 //InterfaceA.java文件
 2 package com.Kevin;
 3 
 4 public interface InterfaceA {
 5 
 6     void methodA();
 7 
 8     String methodB();
 9 
10     // 默认方法
11     default int methodC() {
12         return 0;
13     }
14 
15     // 默认方法
16     default String methodD() {
17         return "这是默认方法...";
18     }
19 
20     // 静态方法
21     static double methodE() {
22         return 0.0;
23     }
24 }

在接口InterfaceA中声明了两个抽象方法methodA和methodB,两个默认方法methodC和methodD,还有声明了静态方法methodE。接口中的默认方法类似于类中具体方法,给出了具体实现,只是方法修饰符是default。接口中静态方法类似于类中静态方法。

实现接口示例代码如下:

 1 //ABC.java文件
 2 package com.Kevin.imp;
 3 
 4 import com.Kevin.InterfaceA;
 5 
 6 public class ABC implements InterfaceA {
 7 
 8     @Override
 9     public void methodA() {
10     }
11 
12     @Override
13     public String methodB() {
14         return "实现methodB方法...";
15     }
16 
17     @Override
18     public int methodC() {
19         return 500;
20     }
21 }

实现接口时接口中原有的抽象方法在实现类中必须实现。默认方法可以根据需要有选择实现(覆盖)。静态方法不需要实现,实现类中不能拥有接口中的静态方法。

上述代码中ABC类实现了InterfaceA接口,InterfaceA接口中的两个默认方法ABC只是实现(覆盖)了methodB。

调用代码如下:

 1 //HelloWorld.java文件
 2 package com.Kevin.imp;
 3 
 4 import com.Kevin.InterfaceA;
 5 
 6 public class HelloWorld {
 7 
 8     public static void main(String[] args) {
 9 
10         //声明接口类型,对象是实现类,发生多态
11         InterfaceA abc = new ABC();
12 
13         // 访问实现类methodB方法
14         System.out.println(abc.methodB());
15 
16         // 访问默认方法methodC
17         System.out.println(abc.methodC());               
18 
19         // 访问默认方法methodD
20         System.out.println(abc.methodD());               
21 
22         // 访问InterfaceA静态方法methodE
23         System.out.println(InterfaceA.methodE());        
24     }
25 }

运行结果:

实现methodB方法...
500
这是默认方法...
0.0

从运行结果可见,代码第17行调用默认方法methodC,是调用类AB中的实现。代码第20行调用默认方法methodD,是调用接口InterfaceA中的实现。代码第23行调用接口静态方法,只能通过接口名(InterfaceA)调用,不能通过实现类ABC调用,可以这样理解接口中声明的静态方法与其他实现类没有任何关系。

Tips:学习了接口默认方法后,有些读者还会有这样的疑问,Java 8之后接口可以声明抽象方法和具体方法,这就相当于抽象类一样了吗?在多数情况下接口不能替代抽象类,例如当需要维护一个对象的信息和状态时只能使用抽象类,而接口不行,因为维护一个对象的信息和状态需要存储在实例成员变量中,而接口中不能声明实例成员变量。

四、总结

 1 接口与类的相似点:

  • 一个接口可以有多个方法。
  • 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
  • 接口的字节码文件保存在 .class 结尾的文件中。
  • 接口相应的字节码文件必须在与包名称相匹配的目录结构中。

 2 接口与类的区别:

  • 接口不能用于实例化对象。
  • 接口没有构造方法。
  • 接口中所有的方法必须是抽象方法。
  • 接口不能包含成员变量,除了 static 和 final 变量。
  • 接口不是被类继承了,而是要被类实现。
  • 接口支持多继承。

 3 接口特性:

  • 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
  • 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
  • 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。

 4 接口与抽象类的区别:

  •  抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
  •  抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
  •  一个类只能继承一个抽象类,而一个类却可以实现多个接口。

5 什么时候使用抽象类和接口

  • 如果你拥有一些方法并且想让它们中的一些有默认实现,那么使用抽象类吧。
  • 如果你想实现多重继承,那么你必须使用接口。由于Java不支持多继承,子类不能够继承多个类,但可以实现多个接口。因此你就可以使用接口来解决它。
  • 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324866549&siteId=291194637