JAVAメソッド呼び出しの理解

16026 ワード

次に、x.f(args)を呼び出し、暗黙的なパラメータxがクラスCのオブジェクトとして宣言されると仮定する.次は呼び出しプロセスの詳細です.1,コンパイラはオブジェクトの宣言タイプとメソッド名を調べます.x.f(param)が呼び出され、暗黙的なパラメータxがCクラスのオブジェクトとして宣言されると仮定します.fという名前が複数あるかもしれませんが、パラメータタイプが異なる方法があります.例えば、方法f(int)および方法f(String)が存在する場合がある.コンパイラは、すべてのCクラスのfというメソッドと、そのスーパークラスのアクセス属性がpublicでfというメソッド(スーパークラスのプライベートメソッドはアクセスできません)を列挙します.これで、コンパイラは呼び出される可能性のあるすべての候補メソッドを取得しました.
2,次に,コンパイラは呼び出し方法の際に提供されるパラメータタイプを調べる.fという名前のすべてのメソッドに、提供されたパラメータタイプと完全に一致するメソッドが存在する場合、メソッドを選択します.このプロセスは、オーバーロード解析(overloadingresolution)と呼ばれます.たとえば、x.f(「Hello」)を呼び出す場合、コンパイラはf(int)ではなくf(String)を選択します.タイプ変換が許可されているため(intはdoubleに変換でき、ManagerはEmployeeに変換できます.など)を選択すると、このプロセスは複雑になる可能性があります.コンパイラがパラメータタイプに一致する方法を見つけていないか、タイプ変換後に複数の方法が一致していることが判明した場合、エラーが報告されます.
メソッドの名前とパラメータのリストをメソッドの署名と呼びます.たとえば、f(int)とf(String)は同じ名前、異なる署名を持つ2つのメソッドです.サブクラスにスーパークラス署名と同じメソッドが定義されている場合、サブクラスのこのメソッドはスーパークラスの同じ署名のメソッドを上書きします.
3 privateメソッド、staticメソッド、finalメソッド、またはコンストラクタの場合、コンパイラはどのメソッドを呼び出すべきかを正確に知ることができ、この呼び出し方式を静的バインドと呼ぶ(static binding).これに対応して、呼び出しの方法は暗黙的なパラメータの実際のタイプに依存し、実行時に動的バインドが実現される.我々が列挙した例では、コンパイラは動的バインド方式でf(String)を呼び出す命令を生成する.
4.プログラムが実行され、動的バインディング呼び出し方法が採用される場合、仮想マシンは必ずxが参照するオブジェクトの実際のタイプに最も適したそのクラスの方法を呼び出す.xの実際のタイプはDであり、それはCクラスのサブクラスであると仮定する.Dクラスが方法f(String)を定義した場合、直接それを呼び出す.そうでなければ、Dクラスのスーパークラスでf(String)を探して、このように類推する.
メソッドを呼び出すたびに検索が行われ、時間のオーバーヘッドがかなり大きいため、仮想マシンはクラスごとにメソッドテーブルを作成しておきます.(method table)すべてのメソッドの署名と実際の呼び出しのメソッドがリストされています.これにより、実際の呼び出しメソッドの場合、仮想マシンはこのテーブルのみを検索すればよいのです.前述の例では、仮想マシンはDクラスのメソッドテーブルを検索して、呼び出しf(Sting)と一致するメソッドを探します.このメソッドはD.f(String)である可能性もあれば、X.f(String)である可能性もありますああ、ここのXはDのスーパークラスです.super.f(param)を呼び出すと、コンパイラは暗黙的なパラメータスーパークラスのメソッドテーブルを検索することを注意する必要があります.
例として、次のコードを示します.
import java.time.LocalDate;
//  public       ,    
public class j {
    public static void main (String[] args) {
        Manager boss = new Manager("Cal Cracker", 8000, 1987, 12, 15);
        Employee[] staff = new Employee[3];
        staff[0] = boss;
        staff[1] = new Employee("Harry", 50000, 1989, 10,1);
        staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);

        for (Employee e : staff) {
            e.raiseSalary(5);
        }
        for (Employee e : staff)
            System.out.println("name=" + e.getName() + ",salary=" + e.getsalary() + ",hireDay=" + e.getHireDay());
    }
}
// Employee,     ,          
class Employee//            
{
    //                
    //   private    Employee              ,             
    private String name;//            :name  String    
    private Double salary;
    private LocalDate hireDay;//hireDay  LocalDate    
//             public,   public                    。
    public Employee(String n, double s, int year, int month, int day) {
        name = n;
        salary = s;
        hireDay = LocalDate.of(year, month, day);
    }


    public String getName() {
        return name;
    }
    public double getsalary() {
        return salary;
    }
    public LocalDate getHireDay() {
        return hireDay;
    }
    //                 ,           ,          。


    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        this.salary += raise;//this       ,     salary          
    }
}

class Manager extends Employee
{
    private double bonus;

    public Manager(String n, double s, int year, int month, int day)
    {
        super(n, s, year, month, day);
        bonus = 0;
    }

    public double getSalary()
    {
        double getSalary = super.getsalary();
        return getSalary + bonus;
    }

    public void setBonus(double b)
    {
        this.bonus = b;
    }
}


//   Employee   ,      ,                
//       :  Employee[] staff = new Employee[3];
//Employee          : public Employee(String n, double s, int year, int month, int day)
/*
     ,         ,  ,          :
1,         (  :private String name;)
2,             (  :public String getName())
3,             (  : public void raiseSalary())
 */

e Employeeタイプとして宣言します.EmployeeクラスにはgetSalaryというメソッドが1つしかありません.このメソッドにはパラメータがありません.したがって、ここではリロード解析の問題を心配する必要はありません.getSalaryはprivateメソッド、staticメソッド、finalメソッドではないため、動的バインドが使用されます.仮想マシンはEmployeeとManagerの2つのクラス生成メソッドテーブルです.Employeeのメソッドテーブルに表示されます.
Employee: getName() -> Employee.getName() getSalary() -> Employee.getSalary() getHireDay() -> Employee.getHireDay() raiseSalary(double) -> Employee. raiseSalary(double)
実際、上記の方法は完全ではありません.EmployeeクラスにはスーパークラスObjectがあります.Employeeクラスはこのスーパークラスから多くの方法を継承しています.ここでは、Objectメソッドを省略します.
Managerメソッドテーブルは少し異なります.3つのメソッドは継承され、1つのメソッドは再定義され、もう1つのメソッドは新しく追加されました.
Manager: getName() -> Employee.getName() getSalary() -> Manager.getSalary() getHireDay() -> Employee.getHireDay() raiseSalary(double) -> Employee. raiseSalary(double) setBonus(double) -> Manager.setBonus(double)
実行時にe.getSalary()を呼び出すの解析手順は、1.まず、仮想マシンがeの実際のタイプのメソッドテーブルを抽出する.Employee、Managerのメソッドテーブルであってもよいし、Employeeクラスの他のサブクラスのメソッドテーブルであってもよい.2.次に、仮想マシンはgetSalary署名を定義するクラスを検索する.このとき、仮想マシンはどのメソッドを呼び出すべきか知っている.3.最後に、仮想マシンはメソッドを呼び出す.動的バインディングには、既存のコードを変更することなくプログラムを拡張できるという非常に重要な特性があります.新しいクラスExecutiveを追加し、変数eがこのクラスのオブジェクトを参照する可能性があると仮定します.呼び出しe.getSalary()を含むのコードを再コンパイルします.eがExecutiveクラスのオブジェクトを適切に参照すると、自動的にExecutive.getSalary()メソッドが呼び出されます.
メソッドを上書きする場合、サブクラスメソッドはスーパークラスメソッドの可視性を下回ることはできません.特に、スーパークラスメソッドがpublicの場合、サブクラスメソッドは必ずpublicとして宣言する必要があります.サブクラスメソッドを宣言するときにpublic修飾子が漏れてしまうというエラーがよく発生します.この場合、コンパイラはより厳格なアクセス権を提供しようとすると解釈します.