web-dev-qa-db-ja.com

オーバーロードはコンパイル時のポリモーフィズムです。本当に?

オーバーライドとオーバーロードの構文上の違いを知っています。また、オーバーライドは実行時のポリモーフィズムであり、オーバーロードはコンパイル時のポリモーフィズムであることも知っています。しかし、私の質問は、「オーバーロードは本当にコンパイル時のポリモーフィズムですか?メソッドの呼び出しはコンパイル時に本当に解決するのですか?」です。私のポイントを明確にするために、例のクラスを考えてみましょう。

_public class Greeter {
    public void greetMe() {
        System.out.println("Hello");
    }

    public void greetMe(String name) {
        System.out.println("Hello " + name);
    }

    public void wishLuck() {
        System.out.println("Good Luck");
    }
}
_

すべてのメソッドgreetMe(), greetMe(String name), wishLuck()はパブリックなので、それらはすべてオーバーライドできます(オーバーロードされたものを含む)。例えば、

_public class FancyGreeter extends Greeter {
    public void greetMe() {
        System.out.println("***********");
        System.out.println("*  Hello  *");
        System.out.println("***********");
    }
}
_

ここで、次のスニペットを検討してください。

_Greeter greeter = GreeterFactory.getRandomGreeter();
greeter.greetMe();
_

getRandomGreeter()メソッドは、ランダムなGreeterオブジェクトを返します。 Greeterのオブジェクト、またはFancyGreeterGraphicalGreeterなどのサブクラスのいずれかを返す場合があります。 getRandomGreeter()は、newを使用してオブジェクトを作成するか、クラスファイルを動的にロードして、リフレクション(リフレクションで可能だと思います)または他の可能な方法を使用してオブジェクトを作成します。 Greeterのこれらのメソッドはすべて、サブクラスでオーバーライドされる場合とされない場合があります。したがって、コンパイラーは、特定のメソッド(オーバーロードされているかどうか)がオーバーライドされているかどうかを知る方法がありません。正しい?また、ウィキペディアは次のように述べています 仮想関数

Javaでは、すべての非静的メソッドはデフォルトで「仮想関数」です。オーバーライドできないキーワードfinalでマークされたメソッドと、継承されないプライベートメソッドのみが非仮想です。

仮想関数は動的メソッドディスパッチを使用して実行時に解決され、非プライベート、非最終メソッドはすべて仮想(オーバーロードされているかどうかに関係なく)であるため、実行時に解決する必要があります。正しい?

それでは、どのようにしてコンパイル時にオーバーロードを解決できるでしょうか?または、私が誤解したこと、または私が見逃していることはありますか?

24
Jomoos

それがあなたが求めるものであるならば、オーバーロードされたメソッドはまだオーバーライドすることができます。

オーバーロードされたメソッドは、同じ名前を共有していても、異なるファミリーのようです。コンパイラーは、シグニチャーを指定して静的に1つのファミリーを選択し、実行時にクラス階層内の最も具体的なメソッドにディスパッチされます。

つまり、メソッドのディスパッチは2つのステップで実行されます。

  • 最初のものは、利用可能な静的情報を使用してコンパイル時に実行されます。コンパイラは、メソッドのオブジェクトの宣言された型のオーバーロードされたメソッドのリストの中で現在のメソッドパラメータに最も一致するシグネチャのcallを発行します呼び出されます。
  • 2番目のステップは実行時に実行され、呼び出される必要のあるメソッドシグネチャが与えられると(前のステップ、覚えていますか?)、JVMはそれを実際のタイプのレシーバーオブジェクトの最も具体的なオーバーライドバージョンにディスパッチします。

メソッドの引数の型がまったく共変でない場合、オーバーロードはコンパイル時にメソッド名を壊すことと同じです。それらは事実上異なるメソッドであるため、JVMは決してレシーバーのタイプに応じてそれらを交換可能にディスパッチすることは決してありません。

12
fortran

すべての「Greeter」クラスには、void greetMe()void greetMe(String)、およびvoid wishLuck()の3つの仮想メソッドがあります。

greeter.greetMe()を呼び出すと、コンパイラは3つの仮想メソッドのどれをメソッドシグネチャから呼び出す必要があるかを判断できます。 void greetMe()は引数を受け入れないためです。呼び出されるvoid greetMe()メソッドの特定の実装は、greeterインスタンスのタイプによって異なり、実行時に解決されます。

あなたの例では、メソッドのシグネチャはすべて完全に異なるため、コンパイラがどのメソッドを呼び出すかを決めるのは簡単です。 「コンパイル時のポリモーフィズム」の概念を示すためのもう少し良い例は、次のとおりです。

_class Greeter {
    public void greetMe(Object obj) {
        System.out.println("Hello Object!");
    }

    public void greetMe(String str) {
        System.out.println("Hello String!");
    }
}
_

このグリータークラスを使用すると、次の結果が得られます。

_Object obj = new Object();
String str = "blah";
Object strAsObj = str;

greeter.greetMe(obj); // prints "Hello Object!"
greeter.greetMe(str); // prints "Hello String!"
greeter.greetMe(strAsObj); // prints "Hello Object!"
_

コンパイラーは、コンパイル時の型を使用して最も具体的に一致するメソッドを選択します。そのため、2番目の例が機能し、void greetMe(String)メソッドを呼び出します。

最後の呼び出しは最も興味深いものです。strAsObjの実行時の型はStringですが、Objectとしてキャストされているため、コンパイラーはそれを認識しています。したがって、コンパイラがその呼び出しに対して見つけることができる最も近い一致は、void greetMe(Object)メソッドです。

14
vaughandroid

ポリモーフィズムとは何ですか?

私にとってエンティティを複数の形式で表すことができる場合、そのエンティティはポリモーフィズムを示すと言われます。

ここで、この定義をJava構成に適用しましょう:

1)演算子のオーバーロードはコンパイル時のポリモーフィズムです。

例えば、 +演算子を使用して、2つの数値を追加することができますOR 2つの文字列を連結します。これは、コンパイル時のポリモーフィズムを厳密に言うポリモーフィズムの例です。

2)メソッドのオーバーロードはコンパイル時のポリモーフィズムです。

たとえば、同じ名前のメソッドに複数の実装を含めることができます。これは、コンパイル時のポリモーフィズムでもあります。

It's compile-time because before execution of program compiler decides the flow of program i.e which form will be used during run-time.

)メソッドのオーバーライドは実行時のポリモーフィズムです。

たとえば、同じシグニチャを持つメソッドは、複数の実装を持つことができます。それは実行時のポリモーフィズムです。

4)派生クラスの代わりに使用される基本クラスは実行時ポリモーフィズムです。

たとえば、interface参照はその任意のインプリメンターを指すことができます。

It's run-time because the flow of program can't be known before execution i.e. only during run-time it can be decided that which form will be used.

少し晴れるといいですね。

10
Azodious

この点でのオーバーロードは、動的ディスパッチではなく、コンパイル時に関数のタイプが静的に決定されることを意味します。

実際に舞台裏で行われるのは、タイプが「A」と「B」の「foo」という名前のメソッドに対して、2つのメソッドが作成されることです(「foo_A」と「foo_B」)。どちらを呼び出すかは、コンパイル時に決定されます(foo((A) object)またはfoo((B) object)結果foo_A呼び出されているまたはfoo_B)。したがって、ある方法でこれはisコンパイル時のポリモーフィズムですが、実際のメソッド(つまり、クラス階層のどの実装を使用するか)は実行時に決定されます。

3
scravy

電話をかけることに強く反対している メソッドのオーバーロード なので コンパイル時のポリモーフィズム
私はそれに賛成だ メソッドのオーバーロード 静的バインディング(コンパイル時)ですが、その中にポリモーフィズムは見られませんでした。

明確にするために、自分の意見を質問に入れようとしました。参照できます このリンク。