[Work/Class/Java with Processing/6_JavaStandardLibraries]

アート系学生のためのJAVA with Processing - その6-1 オーバーロードとGenerics

オーバーロード(Overload)

関数のオーバーロード

関数のオーバーロード(Overload)とは「同じ名前の関数だが,引数や戻り値の型や種類が違う関数が複数ある」ことを言う.

例えば,二つの数を足すaddという関数を考えてみる.

Javaの場合,変数の型が明確なので,本来であればaddInteger(int a, int b){}addFloat(float a, float b){}の二つの関数を定義をしなければならない.しかし本質的に同じ機能を持つのに名前が複数あるのはややこしい.

そこでaddという関数の名前は同じものとし,add(int a, int b){}add(float a, float b){}の二つの関数を定義する.このadd関数を呼ぶ時には引数の型が判別され,適切な方が自動的に呼ばれる.

同様に,引数の数が違う場合も関数のオーバーロードが可能である.

コンストラクタのオーバーロード

Javaでは「コンストラクタのオーバーロード」が頻繁に行われる.

例えばこれまでBallクラスのコンストラクタではWindow幅とWindow高さを与えてきた.ここで,

  1. Window幅とWindow高さのみが与えられた時 → 初期X座標, 初期Y座標,色情報等はランダムで決定される
  2. Window幅とWindow高さ以外に初期X座標,初期Y座標が与えられた時 → それに従う

と2通りのコンストラクタを備えるクラスを考えてみる.

class Ball{
  int windowWidth, windowHeight;
  int xPos, yPos;
  int rColor, gColor, bColor;
  
  Ball(int windowWidth, int windowHeight){
    this.windowWidth = windowWidth;
    this.windowHeight = windowHeight;
    xPos = (int)random(windowWidth);
    yPos = (int)random(windowHeight);
  }

  Ball(int windowWidth, int windowHeight, int xPos, int yPos){
    this(windowWidth, windowHeight);
    this.xPos = xPos;
    this.yPos = yPos;
  }

  ……コンストラクタやメンバ関数の定義が続く
}

というように,引数の数が違うコンストラクタを複数用意し,それぞれ違った処理を行うように定義することができる.一つのコンストラクタ内で別のコンストラクタを呼び出すこともでき,これを有効活用すると,様々な場合のコンストラクタを少ないコード量で定義することが可能になる.

Generics

上記の「intを処理する関数とfloatを処理する関数が,同じ内容である場合,オーバーロードという方法によって,同じ関数名で複数の関数を定義することができる」をさらに拡張し「一つの定義しか作らなくても,色々なデータ型に対応できる」ものをGenericsという.

JavaのGenericsは,用意されている標準ライブラリ,特に次項で解説するCollectionを「使うために知っておくべき知識」程度のもので,標準ライブラリが充実しているJavaではこれを使って積極的にクラスを設計することは少ない.

(むしろ標準ライブラリを継承して「Genericsを排除し型を固定した子クラス」を用途に応じて作ることが基本である)

例えばGenericsに対応した関数定義は以下のように書かれる.

public static <T> void myFunction(T t){
  //処理の内容.
  return t;
}

これは「Tというクラスのtというオブジェクトを引数に取り,同じくTというクラスのオブジェクトの戻り値を返す」関数である.

同様に「Tというクラスのオブジェクトを保持し,色々操作するためのインスタンスメソッドを持つクラス」は以下のように書く.

class MyGenericClass<T>{
  T t; //インスタンス変数
  
  // 例えばコンストラクタで入力して初期化する
  MyGenericClass(T t){
    this.t = t;
  }

  // 引数がないコンストラクタの場合,その後setメソッドを定義して代入する
  MyGenericClass(){
  }

  set void setValue(T t){
    this.t = t;
  }

  //もしくはコンストラクタの中でT型のクラスのオブジェクトインスタンスをnewしてthis.tに入れるとか.

この<T>もしくはTを「型変数」と呼ぶ.

このクラスのオブジェクトインスタンスをInteger型で作る場合,以下のように書く.

MyGenericClass<Integer> myObject = new MyGenericClass<Integer>();

この場合,「Integerクラスをバインドしたオブジェクトインスタンスを作る」という言い方をする.

もちろん上記のクラスはオブジェクトインスタンス化の時にFloatやらStringやらでもバインドすることができる.

前述のようにJavaでは標準ライブラリのCollectionを利用するときに意識する程度だが,標準ライブラリが貧弱なC++ではこのGenericsはテンプレートという概念を駆使してコードを記述するためのための非常に重要な要素となってくる.