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 类被认为是多态的,因为它具有多重继承。对于上述示例,以下内容正确 -

  • 鹿是一种动物
  • 鹿是素食者
  • 鹿就是鹿
  • 鹿是一个物体

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

例子

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

所有引用变量 d、a、v、o 都引用堆中的同一个 Deer 对象。

例子

在此示例中,我们通过创建 Deer 对象并将其分配给超类或实现的接口的引用来展示上述概念。

interface Vegetarian{}
class Animal{}
public class Deer extends Animal implements Vegetarian{
	public static void main(String[] args) {
		Deer d = new Deer();
		Animal a = d;
		Vegetarian v = d;
		Object o = d;
		
		System.out.println(d instanceof Deer);
		System.out.println(a instanceof Deer);
		System.out.println(v instanceof Deer);
		System.out.println(o instanceof Deer);
	}	
}

输出

true
true
true
true

虚拟方法

在本节中,我将向您展示 Java 中重写方法的Behave如何让您在设计类时利用多态性。

我们已经讨论了方法重写,其中子类可以重写其父类中的方法。重写的方法本质上隐藏在父类中,除非子类在重写方法中使用 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();
   }
}

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;
   }
}

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;
   }
}

输出

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 对象。一个使用 Salary 参考资料s,另一个使用 Employee 参考资料e

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

e上的 mailCheck()非常不同,因为e是 Employee 引用。当编译器看到e.mailCheck()时,编译器会看到 Employee 类中的 mailCheck() 方法。

这里,在编译时,编译器使用 Employee 中的 mailCheck() 来验证该语句。然而,在运行时,JVM 会调用 Salary 类中的 mailCheck()。

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