このページではJavaオブジェクト指向の演習問題をご用意してみた。
時間があるときにスキルアップとして、トライしてみていただければと思う。
Javaオブジェクト指向の演習問題
オブジェクト指向とはなんぞや?という方は下記を先に確認いただくのが良い。
解答方法
・入力値は問題で定められた規則に従い、正しく入力されるものとし、入力ミスなどは考慮しなくてよい。
問題1(継承)
以下のプログラムを参考にし、以下のAbstractAutomobileクラス(自動車クラス)を継承したMyCarクラスを作成しなさい。
継承元となるクラス
public abstract class AbstractAutomobile {
/**
* 車種区分
*/
protected enum CarType {
LIGHT_CAR, //軽自動車
STANDERD_CAR, //普通者
MEDIUM_CAR, //中型車
LARGE_CAR, //大型車
OVERSIZE //特大車
} ;
/**
* コンストラクタ
*/
protected AbstractAutomobile(){
}
/**
* コンストラクタ
* @param carType 車種区分
*/
protected AbstractAutomobile(CarType carType){
this.setCarType(carType) ;
}
/**
* 車種区分
*/
private CarType carType;
/**
* @return 乗車定員
*/
public abstract int getCapacity();
/**
* @return 車種(モデル)
*/
public abstract String getCarModel();
/**
* @return 車種区分
*/
public CarType getCarType() {
return carType;
}
/**
* @param carType 車種区分
*/
public void setCarType(CarType carType) {
this.carType = carType;
}
/**
* @return 車種区分(文字列)
*/
public String getCarTypeString(){
String typeName = "" ;
if(this.carType.equals(CarType.LIGHT_CAR)){
typeName = "軽自動車";
}else if(this.carType.equals(CarType.STANDERD_CAR)){
typeName = "普通者";
}else if(this.carType.equals(CarType.MEDIUM_CAR)){
typeName = "中型車";
}else if(this.carType.equals(CarType.LARGE_CAR)){
typeName = "大型車";
}else if(this.carType.equals(CarType.OVERSIZE)){
typeName = "特大車";
}
return typeName ;
}
MyCarクラスは、以下の条件とする。
- 車種区分:普通者
- 車種(モデル):自家用ワンボックス車
- 乗車定員:7人
作成したクラスは、以下のMainクラスを使用して実行する
public class Main {
public static void main(String[] args) {
MyCar car = new MyCar();
System.out.println("車種(モデル)は" + car.getCarModel() + "です");
System.out.println("乗車定員は" + car.getCapacity() + "人です");
System.out.println("車種区分は" + car.getCarTypeString() + "です");
}
}
実行例:
- 車種(モデル)は自家用ワンボックス車です
- 乗車定員は7人です
- 車種区分は普通者です
問題2(インターフェースの実装)
4つの項目を持つCSV(カンマ区切り)ファイルを読み込み、指定された順序に並び替えて表示するプログラムを作成しなさい。
4つの項目はそれぞれ
・日付
・最高気温
・最低気温
・平均気温
となっており、表示する順序は
・最高気温の高い順
・最低気温の高い順
・平均気温の高い順
・日付の古い順
の4つの順序の優先順位とし、表示時の列順序も同様とする。
なお、読み込みファイルは「data」を使用すること。
また、プログラムの作成においては、並び替えの順序判定を行う為のTemperature Comparatorクラスを作成し、Collectionsクラスによるソートを利用するものとする。
実行例(「data.csv」を利用した場合)
32,19.7,24.9,2016/10/4 31.3,21.4,26,2016/10/6 27.7,17,21.9,2016/10/20 27.6,18.6,23.1,2016/10/2 26.5,20.1,22.7,2016/10/3 26.4,21.5,24,2016/10/5 26.3,16.7,20.9,2016/10/18 25.7,18.6,22.3,2016/10/9 25.7,12.6,19.1,2016/10/26 25.3,18.2,21.2,2016/10/8 24.9,19,21.4,2016/10/7 23.9,18.4,20.8,2016/10/19 23.3,13.2,19,2016/10/27 23.2,13.7,17.9,2016/10/12 22.9,12.7,17.4,2016/10/16 22.7,11.8,16.7,2016/10/15 22.3,14.7,18.1,2016/10/23 21.8,17.6,19.7,2016/10/1 21.4,15.4,18.2,2016/10/21 20.8,11.6,15.5,2016/10/29 20.1,16.2,17.6,2016/10/11 19.6,17.4,18.4,2016/10/10 19.5,15.7,17.5,2016/10/17 19.5,12.6,15.6,2016/10/14 19.1,11.9,14.9,2016/10/24 19.1,9.8,14.3,2016/10/31 18.3,14.7,16.7,2016/10/22 18.2,15.3,16.5,2016/10/13 17.2,10,14,2016/10/25 14.8,10.1,12.2,2016/10/28 13.5,9.6,11.9,2016/10/30
以下、回答
↓
↓
↓
↓
問題1 解答/解説
解答例
public class MyCar extends AbstractAutomobile {
protected MyCar() {
super(CarType.STANDERD_CAR);
}
@Override
public int getCapacity() {
return 7;
}
@Override
public String getCarModel() {
return "自家用ワンボックス車";
}
}
オブジェクト指向に関する問題だ。
オブジェクト指向を学ぶ際に、例え話として車を表すCarクラスとバスやトラックなどを表すクラスの関係を用いることがある。実際にどのような形で使われるのかを確認する問題と考えていただければと思う。
抽象化
解答例を見て、コード量の少なさに驚いた方もいるかもしれない。
継承元となっているAbstractAutomobileクラスには各種メソッドが用意されており、実体となるMyCarクラスでは、その「差」になる部分だけを用意すれば良い。
また、AbstractAutomobileクラスは継承されることを前提とした「abstract」で定義されており、継承したクラスが用意すべきメソッド(getCapacity、getCarModel)を指定している。
このように「abstract」が指定されたクラスやメソッドを抽象クラス、抽象メソッドと呼び、このようなクラス・メソッドを定義する事を「抽象化」と呼ぶ。
抽象化は実体を宣言するのでは無く、継承先のクラスに実装を求めるものだ。
Javaをはじめとするオブジェクト指向言語では、この抽象化によって、オブジェクトにバリエーションを持たせる手法が多い。
最も身近に利用されるのが、java.io.InputStreamやjava.io.OutputStreamだろう。
InputStreamは、データ読み込みの為の基本的な機能を備えているが、その読み込む媒体によって実装を分けている。
ファイルを読み込む為にはFileInputStreamを用いるし、テキストデータであればStringBufferInputStream、バイナリデータであればByteArrayInputStreamを用いる。
いずれも、データを読み込む為のread()メソッドを実装しており、InputStreamで用意したread(byte[] b)やread(byte[] b,int off,int len)などを利用する事も可能だ。
プログラム作成過程には、「プログラム設計」と呼ばれる、どのようなクラスやメソッドを作成すべきかを検討する段階があるが、オブジェクト指向言語を利用する場合には、抽象化と継承を効率よく利用し、機能の共通化を図るとスマートなソースコードが書けるだろう。
問題2 解答/解説
解答例
public class Main {
public static void main(String[] args) {
try{
FileReader file = new FileReader("data.csv");
BufferedReader buff = new BufferedReader(file);
String text;
ArrayList<String[]> tempList = new ArrayList<String[]>();
while ((text = buff.readLine()) != null) {
String data[] = text.split(",");
tempList.add(data) ;
}
file.close();
① TemperatureComparator tc = new TemperatureComparator() ;
Collections.sort(tempList, tc) ;
for(String data[] :tempList){
System.out.println(data[1] + "," + data[2] + "," + data[3] + "," + data[0]);
}
}catch(FileNotFoundException fnoe){
fnoe.printStackTrace();
}catch(IOException ioe){
ioe.printStackTrace();
}
}
}
public class TemperatureComparator implements Comparator<String[]> {
@Override
public int compare(String[] o1, String[] o2) {
//最高気温比較
int maxTemp1 = (int)(Double.parseDouble(o1[1]) * 10);
int maxTemp2 = (int)(Double.parseDouble(o2[1]) * 10);
if(maxTemp1 < maxTemp2){
return 1 ;
}else if(maxTemp1 > maxTemp2){
return -1 ;
}else{
//最低気温比較
int minTemp1 = (int)(Double.parseDouble(o1[2]) * 10);
int minTemp2 = (int)(Double.parseDouble(o2[2]) * 10);
if(minTemp1 < minTemp2){
return 1 ;
}else if(minTemp1 > minTemp2){
return -1 ;
}else{
//平均気温比較
int avgTemp1 = (int)(Double.parseDouble(o1[3]) * 10);
int avgTemp2 = (int)(Double.parseDouble(o2[3]) * 10);
if(avgTemp1 < avgTemp2){
return -1 ;
}else if(avgTemp1 > avgTemp2){
return 1 ;
}else{
//日付を比較
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
try {
Date date1 = sdf.parse(o1[0]);
Date date2 = sdf.parse(o2[0]);
if(date1.before(date2)){
return -1 ;
}else if(date2.before(date1)){
return 1 ;
}else{
return 0 ;
}
} catch (ParseException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
}
}
return 0;
}
}
interfaceの実装に関する問題だ。
まず、Mainクラスの処理だが、こちらはCSVファイルを読み込んでカンマ区切りの処理を行っているだけなので、詳細は割愛する。
注目すべきは①のTemperatureComparatorを生成している個所と、その次のCollections.sort()メソッドを呼び出している個所だ。
Collections.sort()メソッドは、Listオブジェクトの並び替えを行うためのメソッドで、Collectionsクラスの政的メソッド(static)として定義されている。
静的クラスなので、インスタンスを生成する必要なく利用可能だ。
このメソッドに対し、
- 並び替えを行うリストオブジェクト
- 並び替えのルール(順序付けの判定)を実装したTemperatureComparatorクラスのオブジェクト
を渡すことで、リストオブジェクトの中身を並び替えてもらうことが可能なのだ。
次に、TemperatureComparatorクラスのソースを確認してみよう。
まず、クラス宣言後半の、「implements Comparator」という記述に注目してもらいたい。implementsは実装するインターフェースを記述する際のもので、ここでは「Comparatorインターフェースを実装する」とう意味を表している。
インタフェースの実装とは何か?
ここで「インターフェースの実装」とは何かを整理しておこう。
簡単に言ってしまうと、インターフェースとはプログラムの実体部分を持たず、その実装されるクラス(ここではTemperatureComparatorクラス)にその実体部分を持つという事になる。
インターフェースとなるクラスでは、メソッドの型(引数や戻り値の型)だけを定めており、このメソッドを抽象メソッドと呼ぶ。
抽象クラスでは、そこに定められる抽象メソッドにはabstractの記述が必要だったが、インターフェースでは、そこに定められるメソッドはすべて抽象メソッドであるため、abstractの記述は不要だ。
Comparatorインターフェースにはcompare()メソッドとequals()が抽象メソッドとして定められている。
compare()メソッドでは、2つのオブジェクトを自身で定める基準に従って「どちらが大きいか」の順序付けを行う。
equals()メソッドでは、2つのオブジェクトが「等しい」とされるかどうかを判断する。
Comparatorインターフェースを実装するクラスでは、この2つのメソッドを用意しなければならない。
模範解答では、TemperatureComparatorクラスを
public class TemperatureComparator implements Comparator<String[]>
と定義しており、String配列を比較するルールを定めなければならない。
compare()メソッドでは、問題の指示に従い、
・最高気温の高い順
・最低気温の高い順
・平均気温の高い順
・日付の古い順
の4つの基準で大小を判定している。
この比較結果により、List<String[]>が並び替えられ、出力結果となっている。
また、観察力の鋭い方は、TemperatureComparatorクラスがequals()メソッドを実装していないことに気が付くだろう。
本来であればString配列を題意に従って比較し、同一であることを判断する為のequals()メソッドを実装するべきなのだが、ここでは省略している。
省略といっても、equals()メソッドが無いのではなく、TemperatureComparatorクラスは親クラスとなっているjava.lang.Objectクラスのequals()メソッドを継承しており、Comparatorインターフェースのequals()メソッドの実装としては、java.lang.Objectクラスのequals()メソッドが使用されることになる。
単なる並び替え処理の場合ではあまり問題にならないため、今回の解答例ではequals()メソッドを省略しているが、Comparatorを使用する一部のクラスでは、equals()メソッドを正しく実装しなければならないこともあるので注意してもらいたい。
「正しく実装」とは、compare()メソッドが「0」を返すケースと、equals()メソッドがtrueを返すケースが同じになるような実装を言う。
まとめ
このページではJavaオブジェクト指向の演習問題を2つご用意してみた。ぜひ回答を見る前にトライをしていただければ幸いだ。



