【Java入門】第9回 クラスとインスタンス|Javaオブジェクトの基本
2024.06.07
今回はJavaの基本要素であるクラスと、その実体であるインスタンスについて、基本的な内容を説明します。
目次
クラスの利用
クラスについて
Java言語におけるクラスとは、プログラムを構成する基本の単位で、保持する情報(メンバ変数)や処理(メソッド)を、目的ごとにひとまとまりで記述します。
Java言語には様々なクラスが準備されており、主な例としては文字列を扱うためのStringクラスや、大きな数値を扱うためのBigDecimalクラスなどがあげられます。
また、独自のクラスを作成して実行したり、他のクラスや他のプログラムで利用したりすることもできます。
クラスの定義
次に、クラス名とクラス内に定義する要素について説明したいと思います。
クラス名
Javaのクラス名は、通常は先頭をアルファベット大文字で宣言します。
(小文字でも作成できてしまうので、意識して大文字で始めるように注意してください)
利用できる文字や、命名のルールは変数名のルールと同じです。
複数個の単語から命名する場合は、先頭が大文字のキャメルケースで命名します。
※この「先頭が大文字のキャメルケース」は、「アッパーキャメルケース」「パスカルケース」とも呼ばれますが、これらは全て同じ記法です。
ここでは計算を行うクラスとして、Calculatorという名前でクラスを作成してみます。
/**
* 計算機クラス
*/
public class Calculator {
// ここの間にクラスの持つ要素を記述します
}
次に、Calculatorクラスに計算を行うための要素を追加していきます。
メンバ変数
メンバ変数はそのクラスが保持する情報(の変数)の定義です。
一般的にはクラスの直下(メソッドの外側)に定義します。
クラス宣言の内側でメソッドの外側であればどこでも設定できますが、メソッドの間などあちこちに定義してしまうと後で確認が大変になるので、基本的にはクラスの上部にまとめて宣言するようにしましょう。
ここでは、計算結果を保持するために、Calculatorクラスに int型の変数 resultを定義してみます。
/**
* 計算機クラス
*/
public class Calculator {
/** メンバ変数・計算結果 */
int result;
}
メソッド
メソッドはそのクラスで実際に行う処理を記述します。これもクラス宣言の内側に定義します。
メソッドでは処理を実行した結果として、何らかの値(プリミティブ型または参照型)を処理の呼び出し元に返却(return)することができます。
// 処理した結果をintの値で返却するメソッドの定義例
public int methodInt(){
// ここに何らかの処理を記述
// 実行結果としてintの値を返却(return)する
return 0;
}
値を返さず処理を行う場合は、void という形でメソッドを宣言します。
(void は値を返さないメソッドのためのキーワードで、予約語にも定義されています)
ここでは、計算結果を表示するメソッドとして displayResultメソッドと、加算を行うためのメソッドとして plusメソッドを Calculatorクラスに定義してみます。
/**
* 計算機クラス
*/
public class Calculator {
/** メンバ変数・計算結果 */
int result;
/**
* displayResult メソッド
* 現在の計算結果を標準出力します。
*/
public void displayResult() {
System.out.println("現在の計算結果: " + result);
}
/**
* plus メソッド
* 現在の計算結果に引数の値を加算して、新しく計算結果として保存します。
* 実行結果として、保存された新しい計算結果を返します。
* @param param 現在の計算結果に加算する値
* @return 加算した後の新しい計算結果の値
*/
public int plus(int param) {
result = result + param;
return result;
}
}
コンストラクタ
コンストラクタは、そのクラスのインスタンスが生成された時に実行される処理を定義します。
(インスタンスとは、クラスを利用するためにオブジェクト化(実体化)することです。詳細についてはこの後で説明します)
コンストラクタに定義された処理は、インスタンスが生成されるたびに、各々のインスタンスに対して実行されます。
public class Sample {
// コンストラクタ(引数なし)の定義例
// コンストラクタはクラス名と同じ名前で記述します
public Sample(){
//ここにクラスの初期処理を記述
}
}
コンストラクタは引数なしのものと、引数ありのものがあり、コンストラクタの記述を省略した場合は「引数なしのコンストラクタ(処理なし)」のみが自動的に設定されます。
public class Sample {
// コンストラクタ(引数あり)の定義例
public Sample(int value){
// 引数valueを利用するクラスの初期処理を記述
}
}
引数なし、引数ありのコンストラクタは1クラス内に同時に定義することも可能です。
また、引数ありのコンストラクタは、引数の種類や数が違えばさらに追加で定義することも可能です。
ただし、引数ありのコンストラクタを定義した場合は、引数なしのコンストラクタは自動的に設定されませんので注意してください。
(引数なし・ありの両方を定義したい場合は、明示的にコンストラクタの記述が必要になります)
ここでは、引数なしのコンストラクタ(10~12行目)と、初期の計算結果として引数を設定するコンストラクタ(15~17行目)の2つのコンストラクタを定義してみます。
/**
* 計算機クラス
*/
public class Calculator {
/** メンバ変数・計算結果 */
int result;
/** コンストラクタ */
public Calculator() {
result = 0;
}
/** コンストラクタ(計算結果を初期設定) */
public Calculator(int value) {
result = value;
}
/**
* displayResult メソッド
* 現在の計算結果を標準出力します。
*/
public void displayResult() {
System.out.println("現在の計算結果: " + result);
}
/**
* plus メソッド
* 現在の計算結果に引数の値を加算して、新しく計算結果として保存します。
* 実行結果として、保存された新しい計算結果を返します。
* @param param 現在の計算結果に加算する値
* @return 加算した後の新しい計算結果の値
*/
public int plus(int param) {
result = result + param;
return result;
}
}
Calculatorクラスは、以下のように引数なし、int型の引数ありのどちらでも、newキーワードでインスタンスを生成することができます。
Calculator calcA = new Calc(); // 初期の計算結果が 0 のインスタンスが生成される
Calculator calcB = new Calc(100); // 初期の計算結果が 100 のインスタンスが生成される
インスタンスの利用
それでは、作成したCalculatorクラスの処理を実際に動かしてみましょう。
Mainメソッドを持つ CalculatorMainクラスを新たに作成して、Calculatorクラスのインスタンスを生成し、メソッドを利用する処理を実行します。
/**
* CalculatorMainクラス
* Calculatorクラスを利用して、計算およぼ結果出力を実行します
*/
public class CalculatorMain {
/**
* Mainメソッド
* @param args
*/
public static void main(String[] args) {
// Calculatorクラスのインスタンスを生成(引数無し ⇒ 初期の計算結果は 0 )
Calculator calc = new Calculator();
// 10加算して結果を出力
calc.plus(10); // 0 + 10 = 10
calc.displayResult();
// さらに20加算して結果を出力
calc.plus(20); // 10 + 20 = 30
calc.displayResult();
}
}
実行結果:
現在の計算結果: 10
現在の計算結果: 30
インスタンス化とは
Javaのクラスは「変数(フィールド)」の種類や「処理の内容」を定義している「型」であるため、実際にプログラム上で動作させるためには、クラスの型から生成されたオブジェクト(実体)を準備する必要があります。
この、クラスの型に従って生成されたオブジェクトのことをインスタンスと呼びます。
インスタンスを利用する場合は、newキーワードを利用したり、クラスが準備しているメソッドを利用するなど、クラスごとに定められた方法でインスタンスを生成して利用します。
ただしStringクラスは例外的に、"こんにちは" のように文字列を「"」(ダブルクォーテーション)で囲んだ値を直接設定して利用することができます。
このダブルクォーテーションで直接囲んだ文字列のことを「文字列リテラル」と呼びます。
// インスタンス化
String str01 = "こんにちは";
String str02 = "hello";
String str03 = "你好";
String str04 = "hola";
java.math.BigDecimal dec01 = new java.math.BigDecimal(1); // int
java.math.BigDecimal dec02 = new java.math.BigDecimal(10F); // float
java.math.BigDecimal dec03 = java.math.BigDecimal.valueOf(100L); // long
java.math.BigDecimal dec04 = java.math.BigDecimal.valueOf(1000.00D); // double
クラスを利用する場合は、パッケージ名からすべての名前で記述するか(※)、import 宣言 をクラスの宣言より前に記述することで、パッケージ名を除いたクラス名だけで記述することができます。
※ java.langパッケージ内のクラス(Stringクラスなど)や、同一パッケージ内のクラスについては、import宣言無しでもクラス名だけで記述できます。
import java.math.BigDecimal ; // BigDecimalをインポート
public class Instances {
// 「java.math.」の部分は記述しなくても利用可能
BigDecimal dec01 = new BigDecimal(1);
BigDecimal dec02 = new BigDecimal(10f);
BigDecimal dec03 = BigDecimal.valueOf(100L);
BigDecimal dec04 = BigDecimal.valueOf(1000.00D);
}
ラッパークラス
Javaでは各プリミティブ型に対応するラッパークラスというものが準備されています。
ラッパークラスでは、プリミティブ型の値をオブジェクトとして扱ったり、参照型変数の値をプリミティブ型の値に変換するためのメソッドなどが提供されます。
プリミティブ型 | ラッパークラス |
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
オートボクシングとアンボクシング
プリミティブ型の値を対応するラッパークラスのインスタンスに変換する(オブジェクトにする)ことを「ボックス化」と呼びます。
ラッパークラスでは、対応するプリミティブ型の値に対して、= で代入することで、値を直接ラッパークラスのインスタンスに変換(オートボクシング・自動ボックス化)したり、逆にラッパークラスのインスタンスをプリミティブ型に変換(アンボクシング・ボックス化解除)することができます。
int i = 10; // プリミティブ型の変数を準備
Integer itgr = null; //ラッパークラスの変数を準備
// オートボクシング
itgr = i; // 10 の値を持つIntegerクラスのインスタンスに変換して、変数itgrに設定
また、Integerクラスにはintの値をインスタンスに変換するためのメソッド(valueOfメソッド)や、インスタンスを値に変換するためのメソッド(intValueメソッド)が準備されています。
Java8までのバージョンでは、Integerクラスのインスタンスはnewキーワードを用いて生成することも可能でしたが、Java9以降では非推奨となっていますので、利用しないようにしましょう。
// Integer.valueOf メソッドでインスタンス化
itgr = Integer.valueOf(i);
// new キーワードでインスタンスを作成(Java8まで。Java9以降は非推奨)
itgr = new Integer(i);
// Integer.intValue メソッドでプリミティブ型に変換
i = itgr.intValue();
クラスの継承
クラスの継承とは
Javaのクラスは、別のクラスの実装を引き継いで、保持する情報や機能を拡張して利用することができます。これをクラスの継承と呼びます。
Javaでは別のクラスを継承したクラスを作成する場合、以下のサンプルのようにextends句を利用してクラスを宣言します。継承するクラス(extends句の後に記述するクラス)は、1つだけ指定できます。
以下の例は、Aクラスを継承してAxクラスを作成する場合のクラスの宣言です。
このとき Aクラスを「継承元」、Axクラスを「継承先」と呼びます。
class Ax extends A
クラスの作成時に具体的な継承元を指定しない場合は、Objectクラス(java.lang.Object)を自動的に継承したクラスとして生成されています。
(例外的に、Objectクラスの継承はプログラムコード上には記述しなくても良いこととなっています)
クラス継承(親子関係)の例
2つのクラスが継承関係にあるとき、継承元のクラスを親クラス(スーパークラス)、継承先のクラスを子クラス(サブクラス)と呼びます。親子関係と継承の仕組みについて、具体的な例と一緒に確認してみましょう。
基本となるクラス(Vehicleクラス)の定義
まず、基本的な乗り物をあらわすクラスを定義してみます。
ここでは、乗り物とは「定員数」「現在の乗員数」「現在の速度」の情報を持つものとして、Vehicleクラスという名前で作成してみます。
/**
* 乗り物クラス
*/
public class Vehicle {
/** 定員数 */
public int capacity;
/** 現在の乗員数 */
public int passenger;
/** 現在の速度 */
public int velocity;
/**
* getRestメソッド:あと何人乗れるかを返します
* @return 定員数から現在の乗員数を引いた数
*/
public int getRest() {
return capacity - passenger;
}
}
Vehicleクラスを継承したクラス(子クラス)の定義
ひとことに乗り物といっても色々な種類がありますので、まずは自転車のクラスとして Bicycleクラスを定義してみます。
Bicycleクラスは Vehicleクラスを継承して(=乗り物クラスの1つとして)作成します。
/**
* 自転車(Bicycle)クラス
*/
public class Bicycle extends Vehicle{
/** タイヤのインチ数 */
public int inch;
/** かご付かどうか */
public boolean hasBusket;
}
このようにクラスを継承することで、Bicycleクラスは Vehicleクラスの持っている要素(定員数、乗員数、速度)に加え、自身で追加した要素(タイヤのインチ数、かごの有無)を持つ乗り物として作成できます。
このとき、Vehicleクラスは Bicycleクラスの親クラス、BicycleクラスはVehicle クラスの子クラスとなります。
「Bicycle」クラスは「Vehicle」クラスの要素を全て引き継いでいますので、「Bicycle」クラスは「Vehicle」クラスでもある(子クラスは親クラスでもある)ということに注目してみてください。
次に、エンジンを搭載した乗り物をあらわすクラスとして MotorVehicleクラスを定義してみます。
これも同じように Vehicleクラスを継承して作成します。
/**
* 原動機(エンジン)付きの乗り物クラス
*/
public class MotorVehicle extends Vehicle {
/** 動力量(エンジンの馬力数など) */
public int power;
/** 最高速度 */
public int maxVelocity;
/**
* アクセルでスピードを上げます
* @param speed 上げたいスピードの量
* @return スピードを上げたあとの現在速度
*/
public int accelerate(int speed) {
if(velocity + speed < maxVelocity) {
velocity = velocity + speed;
} else {
velocity = maxVelocity;
}
return velocity;
}
/**
* ブレーキでスピードを下げます
* @param speed 下げたいスピードの量
* @return スピードを下げたあとの現在速度
*/
public int brake(int speed) {
if(velocity > speed) {
velocity = velocity + speed;
} else {
velocity = 0;
}
return velocity;
}
}
MotorVehicleクラスもVehicleクラスの子クラスとなりますので、Bicycleクラスと MotorCycleクラスは兄弟のような関係になります。
子クラスをさらに継承したクラス
さらに、この「MotorVehicle」クラスを継承したクラスを作成することも可能です。
次に動力付きの乗り物として、バイクと自動車のクラスを各々定義してみましょう。
まず、バイクを MotorVehicleクラスと同じ構成のまま、MotorCycleクラスとして定義してみます。
継承元と全く同じ構成である(メンバ変数やメソッドを追加しない)場合でも、クラスを継承して定義することが可能です。
/**
* バイク(MotorCycle)クラス
*/
public class MotorCycle extends MotorVehicle { }
自動車も MotorVehicleクラスを継承して、Carクラスとして定義してみます。
ここでは、運転席の位置などの情報を持たせてみましょう。
/**
* 自動車(Car)クラス
*/
public class Car extends MotorVehicle {
/** 運転席が左側(左ハンドル)かどうか */
public boolean leftHandle;
/** 寒冷地仕様かどうか */
public boolean coldSpec;
}
これで、Carクラスは Vehicleクラスで定義したもの、MotorCycleクラスで定義したものとあわせ、下図の全てを持つクラスとして定義されます。
また、ここまでに作成したすべてのクラスの親子関係は、下図のようになります。
継承したクラスの利用
継承したクラス同士は親子関係となりますので、以下の内容が成り立ちます。
・Vehicleクラスは、他のすべての乗り物のクラスの親クラスである。
・Bicycleクラスは、Vehicleクラスでもある。
・MotorVehicleクラスは、Vehicleクラスでもある。
・MotorVehicleクラスは、MotorCycleクラスとCarクラスの親クラスでもある。
・MotorCycleクラスは、MotorVehicleクラスでもあり、Vehicleクラスでもある。
・Carクラスは、MotorVehicleクラスでもあり、Vehicleクラスでもある。
子クラスは親クラスでもあるので、親クラスの型で宣言した変数に対しても子クラスのインスタンスを設定することができます。
Vehicle vcar = new Car();
MotorVehicle mvcar = new Car();
Car car = new Car();
ただし親クラスの型で宣言した場合は、変数の型として宣言したクラスまでの情報だけ利用可能になるので注意してください。
int capacity;
int maxVelocity;
boolean isLeft;
// Car型の変数は自身と親クラス(Vehicle、MotorVehicle)の情報を参照できる
capacity = car.capacity;
maxVelocity = car.maxVelocity;
isLeft = car.leftHandle;
// mvcarはMotorVehicle型で変数を宣言したので、Carクラスの情報(leftHandle)は参照できない
capacity = mvcar.capacity;
maxVelocity = mvcar.maxVelocity;
// mvcar.leftHandle は参照できない
// vcarはVehicle型で変数を宣言したので、子クラスに定義された情報(leftHandle、maxVelocity)は参照できない
capacity = vcar.capacity;
// vcar.maxVelocity は参照できない
// vcar.leftHandle は参照できない
また、子クラスのインスタンスは、親クラスの配列にも格納できます。
Bicycle bcl = new Bicycle();
MotorCycle mcl = new MotorCycle();
Car car2 = new Car();
Vehicle[] vclArray = {vcl, mcl, car2}; // 全てVehicleを継承しているので格納できる