자바 다형성



다형성은 동일한 행동이 여러 가지 다른 표현이나 형태를 가질 수 있는 능력입니다.

다형성은 객체의 다양한 표현을 구현한 것입니다.

실제로 F1 키를 누르면

  • AS 3 도움말 문서가 현재 Flash 인터페이스 아래에 나타나는 경우

  • Word의 현재 팝업이 Word 도움말인 경우

  • Windows에서 나타나는 것은 Windows 도움말 및 지원입니다.

동일한 이벤트가 다른 개체에서 발생하면 다른 결과가 생성됩니다.

다형성 존재에 필요한 세 가지 조건:

  • 상속

  • Rewriting

  • 상위 클래스 참조가 하위 클래스 객체를 가리킵니다

예:

Parent p = new Child();

메소드 호출 다형성 사용 먼저 상위 클래스에 해당 메소드가 있는지 확인하고, 존재하지 않으면 컴파일 오류가 발생하며 하위 클래스에서 동일한 이름의 메소드를 호출합니다.

다형성의 이점: 프로그램을 잘 확장할 수 있고 모든 클래스의 객체를 보편적으로 처리할 수 있습니다.

다음은 다형성 인스턴스의 데모입니다. 자세한 설명은 주석을 참조하세요.

public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 catchMouse
  }  
            
    public static void show(Animal a)  {
      a.eat();  
        // 类型判断
        if (a instanceof Cat)  {  // 猫做的事情 
            Cat c = (Cat)a;  
            c.work();  
        } else if (a instanceof Dog) { // 狗做的事情 
            Dog c = (Dog)a;  
            c.work();  
        }  
    }  
}

abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

위 프로그램을 실행하면 출력 결과는 다음과 같습니다.

吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠

virtual method

설계할 때 Java로 소개하겠습니다. 클래스, 반복됨 작성된 메소드의 동작이 다형성에 어떤 영향을 미치는지.

우리는 이미 메서드 재정의에 대해 논의했습니다. 즉, 하위 클래스가 상위 클래스의 메서드를 재정의할 수 있습니다.

하위 클래스 객체가 재정의된 메서드를 호출하면 상위 클래스의 재정의된 메서드가 아닌 하위 클래스의 메서드가 호출됩니다.

부모 클래스에서 재정의된 메서드를 호출하려면 super 키워드를 사용해야 합니다.

/* 文件名 : 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("Employee 构造函数");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   public void mailCheck() {
      System.out.println("邮寄支票给: " + 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 클래스를 상속한다고 가정합니다.

/* 文件名 : Salary.java */
/* 文件名 : Salary.java */
public class Salary extends Employee
{
   private double salary; // 全年工资
   public Salary(String name, String address, int number, double salary) {
       super(name, address, number);
       setSalary(salary);
   }
   public void mailCheck() {
       System.out.println("Salary 类的 mailCheck 方法 ");
       System.out.println("邮寄支票给:" + getName()
       + " ,工资为:" + salary);
   }
   public double getSalary() {
       return salary;
   }
   public void setSalary(double newSalary) {
       if(newSalary >= 0.0) {
          salary = newSalary;
       }
   }
   public double computePay() {
      System.out.println("计算工资,付给:" + getName());
      return salary/52;
   }
}

이제 다음 코드를 주의 깊게 읽고 출력을 시도합니다.

/* 文件名 : VirtualDemo.java */
public class VirtualDemo {
   public static void main(String [] args) {
      Salary s = new Salary("员工 A", "北京", 3, 3600.00);
      Employee e = new Salary("员工 B", "上海", 2, 2400.00);
      System.out.println("使用 Salary 的引用调用 mailCheck -- ");
      s.mailCheck();
      System.out.println("\n使用 Employee 的引用调用 mailCheck--");
      e.mailCheck();
    }
}

위 예제 컴파일 및 실행 결과는 다음과 같습니다.

Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck -- 
Salary 类的 mailCheck 方法 
邮寄支票给:员工 A ,工资为:3600.0

使用 Employee 的引用调用 mailCheck--
Salary 类的 mailCheck 方法 
邮寄支票给:员工 B ,工资为:2400.0

예제 분석

  • 예제에서는 두 개의 Salary 개체가 인스턴스화됩니다. 하나는 Salary를 사용하여 s를 참조하고 다른 하나는 Employee를 사용하여 e를 참조합니다.

  • s.mailCheck()가 호출되면 컴파일러는 컴파일 중에 Salary 클래스에 mailCheck()가 있음을 발견하고 실행 프로세스 JVM은 Salary 클래스의 mailCheck()를 호출합니다.

  • s.mailCheck()를 호출하면 JVM(Java Virtual Machine)이 Salary 클래스의 mailCheck() 메서드를 호출합니다.

  • e는 Employee에 대한 참조이므로 e의 mailCheck() 메서드를 호출하면 컴파일러는 mailCheck() 메서드를 찾기 위해 Employee 클래스로 이동합니다.

  • 컴파일하는 동안 컴파일러는 Employee 클래스의 mailCheck() 메서드를 사용하여 명령문을 확인합니다. 그러나 실행 시 JVM(Java Virtual Machine)은 Salary 클래스의 mailCheck() 메서드를 호출합니다.

위의 전체 과정을 가상 메서드 호출이라고 하며, 그 메서드를 가상 메서드라고 합니다.

Java의 모든 메소드는 이러한 방식으로 표현될 수 있으므로 컴파일 타임에 소스 코드에서 참조되는 변수의 데이터 유형에 관계없이 재정의된 메소드를 런타임에 호출할 수 있습니다.