Java - 多态性

多态性是一种物体采取多种形式的能力.当父类引用用于引用子类对象时,OOP中最常见的多态性使用.

任何可以传递多个IS-A测试的Java对象都被认为是是多态的.在Java中,所有Java对象都是多态的,因为任何对象都会为自己的类型和类Object传递IS-A测试.

重要的是要知道唯一可行的方法是访问对象是通过引用变量.引用变量只能是一种类型.声明后,不能更改引用变量的类型.

如果引用变量未声明为final,则可以将引用变量重新分配给其他对象.引用变量的类型将决定它可以在对象上调用的方法.

引用变量可以引用其声明类型的任何对象或其声明类型的任何子类型.引用变量可以声明为类或接口类型.

示例

让我们看一个例子.

public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}

现在,Deer类被认为是多态的,因为它有多重继承.以下示例适用于以下示例 :

  • 鹿是一种动物

  • A鹿IS-A素食主义者

  • A鹿IS-A鹿

  • 鹿IS-A对象

当我们将引用变量事实应用于Deer对象引用时,以下声明是合法的 :

示例

Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;

所有引用变量d,a,v,o都指向堆中的相同Deer对象.

虚方法

在本节中,我将向您展示Java中重写方法的行为如何允许您在设计类时利用多态性.

我们已经讨论了方法覆盖,其中子类可以覆盖其父级中的方法.重写方法基本上隐藏在父类中,除非子类在重写方法中使用super关键字,否则不会调用该方法.

示例

/* File name : Employee.java */
public class Employee {
   private String name;
   private String address;
   private int number;

   public Employee(String name, String address, int number) {
      System.out.println("Constructing an Employee");
      this.name = name;
      this.address = address;
      this.number = number;
   }

   public void mailCheck() {
      System.out.println("Mailing a check to " + this.name + " " + this.address);
   }

   public String toString() {
      return name + " " + address + " " + number;
   }

   public String getName() {
      return name;
   }

   public String getAddress() {
      return address;
   }

   public void setAddress(String newAddress) {
      address = newAddress;
   }

   public int getNumber() {
      return number;
   }
}

现在假设我们将Employee类扩展为以下 :

/* File name : Salary.java */
public class Salary extends Employee {
   private double salary; // Annual salary
   
   public Salary(String name, String address, int number, double salary) {
      super(name, address, number);
      setSalary(salary);
   }
   
   public void mailCheck() {
      System.out.println("Within mailCheck of Salary class ");
      System.out.println("Mailing check to " + getName()
      + " with salary " + salary);
   }
   
   public double getSalary() {
      return salary;
   }
   
   public void setSalary(double newSalary) {
      if(newSalary >= 0.0) {
         salary = newSalary;
      }
   }
   
   public double computePay() {
      System.out.println("Computing salary pay for " + getName());
      return salary/52;
   }
}

现在,您仔细研究以下程序并尝试确定其输出和减号;

/* File name : VirtualDemo.java */
public class VirtualDemo {

   public static void main(String [] args) {
      Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
      Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
      System.out.println("Call mailCheck using Salary reference --");   
      s.mailCheck();
      System.out.println("\n Call mailCheck using Employee reference--");
      e.mailCheck();
   }
}

这将产生以下结果 :

输出

Constructing an Employee
Constructing an Employee

Call mailCheck using Salary reference --
Within mailCheck of Salary class
Mailing check to Mohd Mohtashim with salary 3600.0

Call mailCheck using Employee reference--
Within mailCheck of Salary class
Mailing check to John Adams with salary 2400.0

在这里,我们实例化两个Salary对象.一个使用薪资参考 s ,另一个使用员工参考 e .

在调用 s.mailCheck( ),编译器在编译时看到Salary类中的mailCheck(),JVM在运行时调用Salary类中的mailCheck().

mailCheck()on e 完全不同,因为 e 是员工参考.当编译器看到 e.mailCheck()时,编译器会看到Employee类中的mailCheck()方法.

这里,在编译时,使用的编译器Employee中的mailCheck()验证此语句.但是,在运行时,JVM在Salary类中调用mailCheck().

此行为称为虚方法调用,这些方法称为虚方法.无论在编译时源代码中使用的引用是什么数据类型,都会在运行时调用重写方法.