0

私は、公式の Oracle チュートリアルを調べていました。ここでは、3 つのクラスのクラス階層の例を使用してポリモーフィズムの概念を紹介しています。Bicycle がスーパークラスで、MountainBike と RoadBike が 2 つのサブクラスです。

2 つのサブクラスが、Bicycle で宣言されたメソッド「printDescription」を、異なるバージョンを宣言することによってオーバーライドする方法を示しています。

そして最後に、このチュートリアルでは、Java 仮想マシン (JVM) が、各変数で参照されるオブジェクトの適切なメソッドを呼び出すことに言及しています。

しかし、ポリモーフィズムに関するチュートリアルでは、「抽象」クラスとメソッドの概念についてはどこにも言及されていません。BicycleのprintDescription()が「抽象的」と宣言されていない限り、実行時のポリモーフィズムはどのように達成されますか? つまり、この例を考えると、どのようなヒントに基づいて、コンパイラーはコンパイル時にメソッド呼び出しを参照型にバインドしないことを決定し、JVM が実行時に処理できるようにする必要があると考えているのでしょうか?

以下は使用例です。

public class Bicycle {
    public int cadence;
    public int gear;
    public int speed;

    public Bicycle(int startCadence, int startSpeed, int startGear) {
      gear = startGear;
      cadence = startCadence;
      speed = startSpeed;
    }

    public void setCadence(int newValue) {
      cadence = newValue;
    }

    public void setGear(int newValue) {
      gear = newValue;
    }

    public void applyBrake(int decrement) {
      speed -= decrement;
    }

    public void speedUp(int increment) {
      speed += increment;
    }

    public void printDescription(){
        System.out.println("\nBike is " + "in gear " + this.gear
         + " with a cadence of " + this.cadence +
         " and travelling at a speed of " + this.speed + ". ");
    }

}

public class MountainBike extends Bicycle {
  private String suspension;

  public MountainBike(
           int startCadence,
           int startSpeed,
           int startGear,
           String suspensionType){
    super(startCadence,
          startSpeed,
          startGear);
    this.setSuspension(suspensionType);
  }

  public String getSuspension(){
    return this.suspension;
  }

  public void setSuspension(String suspensionType) {
    this.suspension = suspensionType;
  }

  public void printDescription() {
    super.printDescription();
    System.out.println("The " + "MountainBike has a" +
        getSuspension() + " suspension.");
  }

}

public class RoadBike extends Bicycle{

  private int tireWidth;

  public RoadBike(int startCadence,
                int startSpeed,
                int startGear,
                int newTireWidth){
    super(startCadence,
          startSpeed,
          startGear);
    this.setTireWidth(newTireWidth);
  }

  public int getTireWidth(){
    return this.tireWidth;
  }

  public void setTireWidth(int newTireWidth){
    this.tireWidth = newTireWidth;
  }

  public void printDescription(){
    super.printDescription();
    System.out.println("The RoadBike"
        " has " + getTireWidth() +
        " MM tires.");
  }
}


public class TestBikes {
    public static void main(String[] args){
        Bicycle bike01, bike02, bike03;

      bike01 = new Bicycle(20, 10, 1);
      bike02 = new MountainBike(20, 10, 5, "Dual");
      bike03 = new RoadBike(40, 20, 8, 23);

      bike01.printDescription();
      bike02.printDescription();
      bike03.printDescription();
      }
}
4

6 に答える 6

4

BicycleのprintDescription()が「抽象的」と宣言されていない限り、実行時のポリモーフィズムはどのように達成されますか?

なぜ抽象クラスが何かを変えると思いますか? 抽象クラスは主に 2 つのことを行います

  1. プログラマーがそれ自体をインスタンス化できないクラスを宣言できるようにし、サブクラス化を強制します。
  2. メソッド抽象を宣言することにより、プログラマーがサブクラスに強制的にメソッドの実装を提供できるようにします。

ポイント 2 は、メソッドが基本クラスで抽象宣言されていない限り、ポリモーフィズムが機能しないことを意味するものではないことに注意してください。むしろ、サブクラスに強制的に実装を提供する機会を開発者に提供します。これは、抽象的な使用法を含まないサブクラス化シナリオでは必要ありません。

それでおしまい。つまり、抽象の概念は Java のポリモーフィズムを補完します。これは言語機能ですが、Java が実行時にメソッドを呼び出すために使用する動的ディスパッチとは何の関係もありません。インスタンスでメソッドが呼び出されるたびに、実行時のインスタンスの型を使用して、使用するメソッド実装が決定されます。

于 2012-10-03T13:02:50.090 に答える
2

Java では、すべてのメソッドが実行時にバインドされます (これは、メソッドを仮想として宣言する C++ で実現できることです)。

したがって、JVM は常にメソッドを正しくディスパッチできます。

実際、Java のメソッド バインディングは決して静的ではありません。これは、常にオブジェクトへの参照を扱っているためです (C++ のようにスタックにオブジェクトを割り当てることはできません)。これにより、JVM は常にオブジェクト参照の実行時の型をチェックするようになります。

于 2012-10-03T13:02:50.777 に答える
2

virtual多くの言語で「このメソッドはサブクラスでオーバーライドできる」という意味のキーワードです。Java にはそのキーワードはありませんが、代わりにすべての非静的メンバー メソッドは仮想であり、オーバーライドすることができます

abstract基本クラスにメソッドの定義がないことをコンパイラに伝える点を除いて、virtual と同じです。基本クラスが実行する有用な機能がない場合に役立つ場合もありますが、基本クラスのメソッドをオーバーライドできる必要はありません。

あなたの場合、 printDescription メソッドには基本クラスの便利な定義があるため、抽象として宣言する必要はありません。これはデフォルトで仮想であるため、サブクラスによってオーバーライド可能であるため、それを示すキーワードは必要ありません。

于 2012-10-03T13:03:24.273 に答える
1

メソッドの抽象化を宣言する必要はありません。ランタイム ポリモーフィズムでは、基底クラスの参照がどのクラス インスタンスを指しているかに基づいて、適切な派生クラス メソッドが呼び出されます。

次の例を考えてみましょう: -

class A {
    public void doSomething() {

    }
}

class B extends A {
    public void doSomething() {
         System.out.println("In B")
    }
}

public class Test {
    public static void main(String args[]) {
          A obj = new B();   // Base class reference and derived class object.

          obj.doSomething();  // Calls derived class B's method and prints `In B`
    }
}

あなたが読んだ声明を引用するには: -

このチュートリアルでは、Java 仮想マシン (JVM) が、各変数で参照されるオブジェクトの適切なメソッドを呼び出すことについて言及しています。

上記のステートメントを正当化するには、上記の例を参照してください。obj基本クラス参照が派生クラスB'sインスタンスを指しているため、そこで B クラス メソッドが呼び出されます。

オブジェクトを指す参照の型は常にコンパイル時にチェックされますが、その参照が指すオブジェクトの型は実行時にチェックされます。

したがって、どのメソッドが呼び出されるかは実行時に決定されます。. 基本クラス メソッドがであるかどうかに関係なく、適切な派生クラス メソッドが呼び出されます。abstract

于 2012-10-03T13:01:56.693 に答える
0

私はこのコードだと思います:

bike01 = new Bicycle(20, 10, 1);       
bike02 = new MountainBike(20, 10, 5, "Dual");       
bike03 = new RoadBike(40, 20, 8, 23);        
bike01.printDescription();       
bike02.printDescription();       
bike03.printDescription(); 

コンパイル時でもすべてのファクト(呼び出すメソッド)がわかっているため、これは実行時ポリモーフィズムの最良の例ではありません。ただし、次のように変更するとします。

Random r = new Random();

if(r.nextInt(2)%2==0)
{
    bike01 = new Bicycle(20, 10, 1)
}
else
{
    bike01 = new MountainBike(20, 10, 5, "Dual");
}

// only at run-time the right function to call is known

bike01.printDescription();

..。

于 2012-10-03T13:14:49.447 に答える
0

これはC++ではありません。Javaでは、各インスタンスの実際のクラスを常に知っているため、printDescription()が呼び出されると、そのクラスの定義が使用されます。ただし、インスタンスへの参照で使用可能なメソッドのみを使用できます(したがって、getMPH()クラスRoadBikeでメソッドを定義し、そのクラスのインスタンスをBike変数で処理する場合、コンパイラーはそれを使用する場合にエラーを発生させます) 。

于 2012-10-03T13:04:53.350 に答える