アクセス修飾子と継承

クラスを継承してサブクラスを定義した場合、サブクラスではスーパークラスのフィールドやメソッドを引き継いで利用できます。ただしフィールドやメソッドに設定されているアクセス修飾子によっては、サブクラスから利用できない場合もあります。ここではオブジェクト指向におけるアクセス修飾子と継承の関係について解説します。

アクセス修飾子と継承

クラスを継承した場合、サブクラスはスーパークラスのフィールドやメソッドを引き継いで利用することができます。ただし、スーパークラスで定義されているフィールドやメソッドに設定されているアクセス修飾子の種類によってはアクセスできない場合もあります。

Java で設定可能なアクセス修飾子には public、protected、何も指定しない(default)、private の 4 種類があり、それぞれアクセスできる範囲が異なります。

public       : どこからでもアクセス可能
protected    : 同じパッケージ、またはサブクラスからアクセス可能
なし(default) : 同じパッケージ内からアクセス可能
private      : 同じクラス内のみアクセス可能

スーパークラスのフィールドやメソッドに public を設定した場合は、サブクラスかどうかに関わらずメソッドやフィールドへアクセスできます。逆に private を設定した場合は同じクラス内からのみアクセスできるので、サブクラスから直接アクセスすることはできません。

アクセス修飾子を設定しなかった場合(default)は、同じパッケージ内であればサブクラスかどうかに関わらずスーパークラスのメソッドやフィールドへアクセスできます。ただし別のパッケージにあるサブクラスからはアクセスできません。

そして protected を設定した場合は、同じパッケージ内か、または別のパッケージ内であってもサブクラスからはスーパークラスのメソッドやフィールドへアクセスできます。

なおサブクラスでスーパークラスのメソッドをオーバーライドする場合も、サブクラスからスーパークラスのメソッドにアクセスできるようにアクセス修飾子が設定されている必要があります。

protectedが使用されるケース

継承を使用する場合に利用されるのが protected です。 protected が付けられたフィールドやメソッドは、同じパッケージ内のクラスだけでなく、別のパッケージであってもサブクラスの中からアクセスすることができます。

外部のクラスには公開したくないが、継承したサブクラスからは利用できるようにしたい場合に使用します。

例えば次のようなクラスがあるとします。

class Product {
  protected String name;

  protected void showName() {
    System.out.println(name);
  }
}

このクラスを継承したサブクラスでは、別のパッケージであっても protected が付いたフィールドやメソッドを利用することができます。

class Food extends Product {
  public void showFood() {
    name = "りんご";
    showName();
  }
}

このように protected を使用することで、サブクラスから利用できるメンバーを定義することができます。

protected は継承して使用することを想定したクラスでよく使用されます。例えば抽象クラスで定義されたメソッドなどに設定されることがあります。(抽象クラスとは、そのクラスのオブジェクトを作成できず、継承されることを前提としたクラスのことです)。

下記では抽象クラスである Product クラスで定義された dispInfo メソッドに protected を設定しています。

public class Sample {
  public static void main(String[] args) {
    Foods f = new Foods();
    f.dispInfo();
  }
}

abstract class Product {
    protected abstract void dispInfo();
}

class Foods extends Product {
  public void dispInfo() {
    System.out.println("食料品です");
  }
}

今回の例ではスーパークラスで protected を設定したメソッドを、サブクラスでオーバーライドするときに public にしています。(オーバーライドするときにアクセス修飾子を変更する場合の詳細はこのあと解説します)。

オーバーライド時のアクセス修飾子の制約

サブクラスでスーパークラスのメソッドをオーバーライドする場合、スーパークラスのメソッドよりアクセス範囲を狭めることはできません。

スーパークラス      サブクラスで設定可能
--------------------------------
public             public
protected          protected / public
default            default / protected / public

次の例のようにスーパークラスで public を指定して定義したメソッドを、サブクラスで private を指定してオーバーライドしています。このように記述してしまうとコンパイルエラーとなります。

class Product {
  public void dispInfo() {
    System.out.println("製品です");
  }
}

class Foods extends Product {
  private void dispInfo() {
    System.out.println("食料品です");
  }
}

スーパークラスのメソッドが public に設定されていた場合は、オーバーライドするメソッドでも public を設定する必要があります。

privateが付けられたフィールドへのアクセス

スーパークラスのフィールドやメソッドに private が付いている場合、そのメンバーはサブクラスから直接アクセスすることはできません。

class Product {
  private String name;
}

このクラスを継承したサブクラスから name フィールドにアクセスしようとするとエラーになります。

class Food extends Product {
  void setData() {
    name = "りんご";   // エラー
  }
}

private が付いたメンバーは同じクラスの中でのみ使用できるため、サブクラスから直接利用することはできません。

private のフィールドをサブクラスから利用したい場合は、スーパークラスで public または protected のメソッドを用意してアクセスするようにします。

class Product {
  private String name;

  public void setName(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }
}

このように getter や setter を用意しておくことで、サブクラスや他のクラスから安全にデータを利用することができます。

-- --

オブジェクト指向におけるアクセス修飾子と継承の関係について解説しました。

( Written by Tatsuo Ikura )

プロフィール画像

著者 / TATSUO IKURA

これから IT 関連の知識を学ばれる方を対象に、色々な言語でのプログラミング方法や関連する技術、開発環境構築などに関する解説サイトを運営しています。