Programming 3 / Java

トップに戻る

多態性・アクセス制御・抽象クラス

継承したメソッドの上書き、実体に応じて処理が変わる多態性、安全にデータを扱う情報隠蔽、共通の形式を定める抽象クラスを学びます。

1

メソッドのオーバーライド

スーパークラスのメソッドを、サブクラスで同じ形のまま上書きすることをオーバーライドといいます。

OverrideSample.java
class Rectangle {
  void setSize(int width, int height) {
    System.out.println("Rectangle");
  }
}

class NamedRectangle extends Rectangle {
  @Override
  void setSize(int width, int height) {
    System.out.println("NamedRectangle");
  }
}
@Override を付けると、正しく上書きできているかコンパイラが確認します。

オーバーロードとの違い

種類定義
オーバーライド継承先で同じシグネチャのメソッドを上書き親と子の play()
オーバーロード同名で引数が異なるメソッドを複数定義abs(int)abs(double)
2

多態性

多態性とは、同じメソッド呼び出しでも、実際のインスタンスによって異なる処理が実行される仕組みです。

変数の型Player
実際のインスタンスnew MusicPlayer()
実行する処理MusicPlayer.play()
多態性の基本
Player player = new MusicPlayer();
player.play(); // MusicPlayer側のplay()を実行

変数の型が Player でも、実体が MusicPlayer なら、Javaは実行時に実体へ対応するメソッドを選びます。

3

フィールドの隠蔽とfinal

子クラスで親クラスと同名のフィールドを宣言すると、子クラス側のフィールドが通常見える状態になります。これはメソッドのオーバーライドとは別の仕組みです。

フィールドの隠蔽
class A {
  int x = 1;
}

class B extends A {
  int x = 5;
}
宣言制限用途
final void execute()子クラスでオーバーライドできない変更させたくない重要な処理
final class Sampleそのクラスを継承できない継承を前提としない設計

StringIntegerSystemMath は代表的なfinalクラスです。

4

パッケージとアクセス修飾子

パッケージは関連するクラスをまとめる単位です。java.lang は自動的に利用できますが、それ以外は通常 import します。

importの例
import java.io.BufferedReader;
import java.io.*;
修飾子同じクラス同じパッケージサブクラスその他
private×××
修飾子なし同じパッケージなら○×
protected×
public
5

情報隠蔽とアクセサ

親クラスの private メンバーは、子クラスからも直接アクセスできません。値は公開されたメソッドを通して扱います。

安全にフィールドを管理する
class Rectangle {
  private int width;

  public int getWidth() {
    return width;
  }

  public void setWidth(int width) {
    if (width >= 0) {
      this.width = width;
    }
  }
}
6

抽象クラスと抽象メソッド

抽象クラスは、共通のフィールドや処理を持ちながら、具体的な処理の一部を子クラスに任せるクラスです。

抽象クラスの基本
abstract class Player {
  abstract void play();
}

class MusicPlayer extends Player {
  @Override
  void play() {
    System.out.println("音楽を再生します");
  }
}
抽象クラスは直接インスタンス化できません。new Player() はコンパイルエラーです。

抽象クラスを継承した通常のクラスは、抽象メソッドを実装する必要があります。

7

そのまま動く多態性サンプル

次のコードを PolymorphismSample.java として保存すると、抽象クラス、オーバーライド、多態性をまとめて実行できます。

PolymorphismSample.java
abstract class Player {
  private String name;

  Player(String name) {
    this.name = name;
  }

  String getName() {
    return name;
  }

  abstract void play();
}

class MusicPlayer extends Player {
  MusicPlayer(String name) {
    super(name);
  }

  @Override
  void play() {
    System.out.println(getName() + ": 音楽を再生します");
  }
}

class MoviePlayer extends Player {
  MoviePlayer(String name) {
    super(name);
  }

  @Override
  void play() {
    System.out.println(getName() + ": 動画を再生します");
  }
}

class GamePlayer extends Player {
  GamePlayer(String name) {
    super(name);
  }

  @Override
  void play() {
    System.out.println(getName() + ": ゲームを開始します");
  }
}

public class PolymorphismSample {
  public static void main(String[] args) {
    Player player1 = new MusicPlayer("音楽プレイヤー");
    Player player2 = new MoviePlayer("動画プレイヤー");
    Player player3 = new GamePlayer("ゲームプレイヤー");

    player1.play();
    player2.play();
    player3.play();
  }
}
コンパイルと実行
javac PolymorphismSample.java
java PolymorphismSample
実行結果を確認する
output
音楽プレイヤー: 音楽を再生します
動画プレイヤー: 動画を再生します
ゲームプレイヤー: ゲームを開始します
8

確認問題

  1. オーバーライドの間違いをコンパイラに確認させる注釈は何ですか。
  2. 親クラスのprivateフィールドを子クラスから安全に取得するには何を使いますか。
  3. Player player = new MusicPlayer();player.play() では、どのクラスの処理が実行されますか。
解答を確認する
  1. @Override です。
  2. 親クラスが公開するgetterなどのメソッドを使います。
  3. 実体に対応する MusicPlayerplay() が実行されます。