メインメニューを開く

オブジェクト指向プログラミングにおいてオーバーライド (override) とは、スーパークラスで定義されたメソッドサブクラスで定義し直し、動作を上書き(変更)することである。

例えば、あるクラスBaseにメソッドprintがあり、あるクラスDerivedがクラスBase継承したとする。そのとき、クラスDerivedはクラスBaseにあるメソッドprintをオーバーライドすることにより、再定義することができる。これはオブジェクト指向プログラミングにおけるポリモーフィズム(多態性)を実現する際によく使われる。

通例、オーバーライドを可能とする条件として、メソッドの名前、引数の数と型の順序、そして戻り値の型が統一されている必要がある[1]

メソッドのオーバーロード多重定義overload)と混同されることがあるが、まったく異なる概念である。

Rubyのようにオーバーロードの概念がなく、引数の型や数の条件がなくメソッド名が同一なだけでオーバーライドが成立するプログラミング言語もある。

目次

オーバーライドの例編集

Java編集

Javaのインスタンスメソッドは既定で仮想メソッドであり、派生クラスでオーバーライド可能である。finalキーワードを指定することでオーバーライドを禁止することもできる。

なお、Java SE 5から導入されたアノテーション@Overrideを用いることで、メソッドがオーバーライドされていることをコンパイラに知らせることができる(正しくオーバーライドされていない場合、コンパイルエラーとなる)。しかし、アノテーションの指定はオプションであり必須ではない。

// 基本クラス。
class Base {
  // コンストラクター
  Base() {
    System.out.println("Base.Base()");
  }

  // インスタンスメソッド
  void print() {
    System.out.println("Base.print()");
  }

  // クラスメソッド
  static void staticPrint() {
    System.out.println("Base.staticPrint()");
  }
}

// 派生クラス。
class Derived extends Base {
  // コンストラクター
  Derived() {
    System.out.println("Derived.Derived()");
  }

  // スーパークラスのインスタンスメソッド print をオーバーライドしている。@Override の指定は必須ではないが推奨される。
  @Override
  void print() {
    System.out.println("Derived.print()");
  }

  // スーパークラスのクラスメソッド staticPrint をオーバーライドすることはできない。隠蔽することになる。
  static void staticPrint() {
    System.out.println("Derived.staticPrint()");
  }
}

public class Main {
  public static void main(String[] args) {
    System.out.println("■ Baseインスタンスメソッドの呼び出し:");
    Base base = new Base();
    base.print();

    System.out.println("■ Derivedインスタンスメソッドの呼び出し:");
    Derived derived = new Derived();
    derived.print();

    System.out.println("■ Base型変数を経由したDerivedインスタンスメソッドのポリモーフィック呼び出し:");
    Base derivedInBaseVariable = new Derived();
    derivedInBaseVariable.print();

    System.out.println("■ Baseクラスメソッドの呼び出し:");
    Base.staticPrint();

    System.out.println("■ Derivedクラスメソッドの呼び出し:");
    Derived.staticPrint();
  }
}

実行結果:

■ Baseインスタンスメソッドの呼び出し:
Base.Base()
Base.print()
■ Derivedインスタンスメソッドの呼び出し:
Base.Base()
Derived.Derived()
Derived.print()
■ Base型変数を経由したDerivedインスタンスメソッドのポリモーフィック呼び出し:
Base.Base()
Derived.Derived()
Derived.print()
■ Baseクラスメソッドの呼び出し:
Base.staticPrint()
■ Derivedクラスメソッドの呼び出し:
Derived.staticPrint()

C#編集

C#のオーバーライドの特徴として、以下が挙げられる。

  • 仮想メソッドおよび抽象メソッドのオーバーライドの際にoverrideキーワードの指定が必要である。
ただし、インターフェイスのメソッドを実装する場合は、overrideの指定は不要 (不可) である。
  • メソッドは既定では非仮想であり、virtualを指定することでオーバーライド可能な仮想メソッドとなる。
  • プロパティインデクサ、イベントも、virtual修飾されている場合はメソッドと同様にオーバーライドの対象となる。
  • sealedキーワードを指定することでオーバーライドを禁止できる。

コードの例を示す。

// 抽象基本クラス。
abstract class Base {
    // 既定では非仮想メソッド。
    public void GoodMorning() { Console.WriteLine("Good morning, Base!"); }

    // virtualを指定することで仮想メソッドとなる。
    public virtual void Hello() { Console.WriteLine("Hello, Base!"); }

    public virtual void Goodbye() { Console.WriteLine("Goodbye, Base!"); }

    // 抽象メソッド(実装を持たない)。
    public abstract void GoodNight();
}

// 派生クラス。
class Derived : Base {
    // 非仮想メソッドはオーバーライドできない。
    // 同名のメソッドで隠蔽する場合、newを指定する。
    // (ここでは指定しないが、さらにvirtualを指定することで仮想メソッドとなる)
    public new void GoodMorning() { Console.WriteLine("Good morning, Derived!"); }

    // 仮想メソッドをオーバーライドする。
    // overrideの指定が必須。
    public override void Hello() { Console.WriteLine("Hello, Derived!"); }

    // 仮想メソッドをオーバーライドする。
    // overrideと共にsealedを指定することで、このクラスを継承した先ではオーバーライドが禁止される。
    public override sealed void Goodbye() { Console.WriteLine("Goodbye, Derived!"); }

    // 抽象メソッドをオーバーライドする。
    // overrideの指定が必須。
    public override void GoodNight() { Console.WriteLine("Good night, Derived!"); }
}

class DerivedDerived : Derived {
    // 非仮想メソッドはオーバーライド不可。
    //public override void GoodMorning() { Console.WriteLine("Good morning, DerivedDerived!"); }

    public override void Hello() { Console.WriteLine("Hello, DerivedDerived!"); }

    // sealedされたメソッドはオーバーライド不可。
    //public override void Goodbye() { Console.WriteLine("Goodbye, DerivedDerived!"); }

    public override void GoodNight() { Console.WriteLine("Good night, DerivedDerived!"); }
}

言語固有の注意点編集

あるスーパークラスとそれを継承したサブクラスを定義する際、JavaやC++ではオーバーライドに関係した問題が起こりうるので注意が必要である(特にスーパークラスの実装者とサブクラスの実装者が異なる場合)。あとからスーパークラスにメソッドを追加したときに、そのメソッドと同じシグネチャのメソッドが既にサブクラスに存在すると、オーバーライドしたつもりがないのに関係のないメソッドをオーバーライドしてしまうという問題が起こる。逆にオーバーライドしたつもりでも、スーパークラスのメソッドシグネチャあるいはアクセシビリティの変更[2]や、サブクラスのメソッド定義時のtypoなどによって正しくオーバーライドできていなかった、といった問題も起こる。これを回避するためにJavaでは@Overrideの指定が推奨されるが、後方互換性を保つため、アノテーションの指定は必須とはなっていない。C++においてもC++11override修飾子が導入されたが、override修飾子の指定はオプションであり必須ではない。

C#では当初からoverride修飾子が必須なのでこの問題は起こらない。

脚注編集

  1. ^ 広義では「シグネチャが同じメソッド」とも言えるが、プログラミング言語ごとに「シグネチャ」の厳密な定義は異なる。
  2. ^ サブクラスから不可視なスーパークラスのメソッドと同じシグネチャを持つメソッドをサブクラスで定義することは可能であり、オーバーライドではなく別のメソッドとして認識される。

関連項目編集