2016年度 発展プログラミング演習 第3講 図書館システム 1日目
本日の内容
1. ウェルカムメッセージを表示する
1-1. まずやってみる
先週学習したクラス定義の基本形を元に,
ファイル名をLibrary.java
として,図書館システムを作成していきましょう.
ここでは,復習を兼ねて一つずつ説明していきます.では,書いてみましょう.
Javaプログラムの一番外側は,必ず public class Foo{ }
で囲まれます.
この部分をクラス宣言と呼びます.
そして,この部分が決まれば,ファイル名もFoo.java
と決まります.
このとき,Foo
をクラス名と呼びます.
また,Javaの名前は,キャメルケース で表すのが一般的です.C言語のスネークケース とは異なりますので,注意しましょう.
1-2. 最初に実行されるもの
Javaでは,最初に実行されるメソッドは,
main
メソッドです.メソッドとは,引数,
返り値を持つ,ひとまとまりの作業(手続き)のことです.
C言語で言うところの関数と同じようなものと思ってもらって結構です.
Java言語のmain
メソッドのシグネチャ
は厳密に決められています.返り値は,void
,引数はString
型の配列です.
void
は何も値を返さないことを表す型です.
また,String
型は,その名前の通り,文字列を表す型です.
main
メソッドの引数では,コマンドラインで与えられた値が文字列型の配列として渡されます.
public class Library{ public static void main(String[] args){ } }
main
メソッドは,クラス宣言の波括弧
の中に書かなければいけません.
大文字,小文字など,一言一句合っている必要があります.
一種のイディオムとして覚えてしまいましょう.
main
メソッドの後ろの括弧内には,
main
メソッドの引数が記述されます.
1-3. ウェルカムメッセージを表示する
Javaでは,ターミナルに文字列を出力するために,System.out.println
メソッドを利用します.
System.out.println
は,改行付きで引数に与えられた値を出力するメソッドです.
改行したくない場合は,System.out.print
を利用しましょう.
また,C言語のprintf
のように利用したい場合は,
System.out.printf
メソッドが利用できます(FAQを参照してください).
では,ウェルカムメッセージを出力してみましょう.出力するメッセージは何でも構いません.
public class Library{ void run(){ this.printWelcomeMessage(); } void printWelcomeMessage(){ System.out.println("ようこそ図書館システムへ."); } public static void main(String[] args){ Library lib = new Library(); lib.run(); } }
run
メソッドの中でウェルカムメッセージを表示するのも良いですが,
左のプログラムのように,ウェルカムメッセージを表示するためだけのメソッドを用意する方がより良いでしょう.
先週のページにも書きました
が,ウェルカムメッセージを表示するためのメソッドを用意することで,run
メソッドの処理内容を見るだけで,どのようなことを行うのかが理解できます.
これからも様々な仕様を追加していきます.その都度細かくメソッドに分けてプログラムを書いていきましょう. それがバグの少ないプログラムに繋がっていきます.
実行すると,次のように出力されます.
$ java Library ようこそ図書館システムへ.
2. 本の情報を表す型を作成する.
2-1. 仕様を確認する.
JavaではC言語でのint
と同じレベルの型が自由に定義できます.
ここでは,本(Book)という型を定義します.
仕様には,本について,次のように記述されています.
本は,タイトル(title),著者(authors),出版社(publisher),出版年(publishYear)を持つ.
仕様を見ると,本にはタイトルと,著者,出版年が含まれているように見えます.
その通り,素直に解釈し,Book
という型には,
title
,authors
,publisher
,
publishYear
という4つの変数を宣言しましょう.
2-2. 型を作成する.
では,実際に型を作成しましょう.といっても,特に変わったことはありません.先ほどのLibrary.java
と同じく,Book.java
を作成します.Library.java
と同じディレクトリに作成してください.
public class Book { }
以降,『Foo
クラスを作成してください』と言われた場合は,
Foo.java
を適切な場所に作成してください.そして,内容を
public class Foo { }
としてください.
Foo
は適宜読み替えるようにしてください.
では,Book
クラスを作成してください.
このプログラムは,main
メソッドを持ちません.
単体では実行できない,Library
から参照されるためのプログラムです.
2-3. 型に変数を追加する.
次に,4つの変数それぞれの型を考えましょう.この図書館システムで最初から利用できる型は,次の4種類です.
- 文字列を表す
String
- 整数値を表す
Integer
- 順番に値が格納する
List
- 連想配列である
Map
(連想配列の詳細は後日)
実は,Javaで利用できる型は1万以上あり,さらにユーザが独自に定義もできます. しかし,この講義では,最初は少数の型で慣れてもらい,徐々に利用する型を増やしていきます.
public class Book { String title; String authors; String publisher; Integer publishYear; }
さて,Book
という型に含まれる4つの変数,title
,
authors
, publisher
, publishYear
はそれぞれどのような型が相応しいでしょうか.title
,authors
,
publisher
は文字列(String
)で表し,
publishYear
は整数(Integer
)で表すのが良さそうです.
これでBook
という型が作成できました.
3. 図書館システムに本のリストを持たせる.
3-1. 概要
最初に2で作成したBook
型の変数に値を代入してみましょう.
Book
型に限らない話ですが,値を作成するには,new
というキーワードを使い,値を作成する必要があります.以下の図のように,
値を作成する前は,実体はメモリ上に確保されていません.
new
で値を作成することではじめてメモリ上に領域が確保されて値が代入できるようになります.
また,値はいくつでも作成でき,値が異なれば互いに影響は受けず,独立した値として扱えます.
3-2. Bookの実体を作成し,タイトルなどを代入する.
では,Library
のrun
メソッド内で,
Book
の実体を作成しましょう.そして,上の図にあるように,
タイトル,著者,出版社,出版年を代入しましょう.
public class Library { void run(){ this.printWelcomeMessage(); Book book = new Book(); book.title = "羅生門"; book.authors = "芥川龍之介"; book.publisher = "青空文庫"; book.publishYear = 1997; System.out.printf("%s (%s) %s, %d%n", book.title, book.authors, book.publisher, book.publishYear); } void printWelcomeMessage(){ System.out.println("図書館システムへようこそ."); } public static void main(String[] args){ Library lib = new Library(); lib.run(); } }
このように,型の中で宣言された変数であっても,ドット(.
)
を介してアクセスできます.実は,System.out.println
も同じようなフォーマットになっています.
この呼び出しは,System
というクラスの変数out
の
println
というメソッドを呼び出しています.
book.title = new String("羅生門"); book.authors = new String("芥川龍之介"); book.publisher = new String("青空文庫"); book.publishYear = new Integer(1997);
String
型,Integer
型はnew
しなくても実体が作成されているように見えますが,これはなぜでしょうか.
本来であれば,左図のように書かなければいけないはずです.
しかし,文字列や整数型をはじめとした数値型はプログラム中で頻繁に使います.
そのため,簡略化して書けるようになっているのです.
しかし,バイナリ中では,以下のようなコードになっています.コンパイラが頑張って変換してくれているんです.
3-3. Bookを作成するメソッドを用意する.
さて,1冊の本の情報を作成できました.しかし,これから何冊もの本を同じように作成するとなると
run
メソッドに似たコード片を何度も書かなくてはならなくなります.
そこで,Book
を作成するcreateBook
メソッドを用意しましょう.
createBook
メソッドは,本のタイトル,著者,出版社,出版年を引数として受け取り,
Book
の実体を返します.
Book createBook(String title, String authors, String publisher, Integer publishYear){ Book book = new Book(); book.title = title; book.authors = authors; book.publisher = publisher; book.publishYear = publishYear; return book; }
このメソッドが作成できれば,先ほどの本の実体作成部分をcreateBook
メソッドの呼び出しに変更してみましょう.
3-4. Bookの集合を保持する変数を用意する.
さて,ここまでで1冊の本の実体を作成できるようになりました. しかし,図書館たるもの複数冊の本が管理できなければいけません. C言語の場合は,同じ型の値を複数扱うときは,配列を使っていました. Javaにも配列はありますが,この図書館システムが扱う本の上限は設定されていません. また,途中で追加,削除が行われる可能性もあります. 配列を使う上でのメリットとデメリットをまとめておきましょう.
- 配列のメリット
- 同じ型の値を複数個扱うときに,一つの変数で扱える.
- 配列のデメリット
- 上限が決まってしまう.
- 途中のインデックスの値を削除すると,あとあと面倒.
配列のデメリットは,以下の2つの図でも表されます.例えば,整数を格納する配列を考えてみましょう. 配列の上限が5と決まれば,その配列はそれ以上の要素を格納できません.6つ目の要素を追加したい場合, 配列をつくり直すところからやり直さなければいけません. また,2番目の要素が削除した後,要素を詰めて空欄をなくしたい場合があります. そのとき,3番目以降の要素を一つずつ左にずらしていく操作が必要になります.
もちろん,上記の問題はプログラムを頑張れば解決できます.
しかし,両方の問題ともに,プログラムの目的からすると,本質的ではありません.
そのような本質的でないところに多大な労力をかけるのは本末転倒です.
そして,近年のプログラム言語では,配列より高度なデータ構造が標準で用意されています.
当然のことながら,Javaでも標準で用意されています.List
型です.
上限がなく,途中の要素を削除しても,自動的に間を詰めてくれます.
List
型を利用するときには,List
型に入れる型を指定する必要があります.
例えば,List
にString
型の値を順番に入れていくときは,
List<String>
型として宣言する必要があります.
また,List
型の実体を作成するときにも,new
を利用しますが,
List<String> list = new List<String>
とはできません.
List
はあくまで型であり,実体を作成できるようにはなっていないためです.
ArrayList<String>
の実体を作成することで,
List<String>
型の変数に代入できるようになります.
さらに,List
やArrayList
を利用するときは,クラス宣言よりも前に
import java.util.*;
という一行を書いておく必要があります.
では,上で作成した Book
型の変数を Library
が持つ
List<Book>
型の変数shelf
に格納しましょう.
import java.util.*; public class Library { void run(){ this.printWelcomeMessage(); List<Book> shelf = new ArrayList<Book>(); shelf.add(this.createBook("羅生門", "芥川龍之介", "青空文庫", 1997)); Book book1 = shelf.get(0); System.out.printf("%s (%s) %s, %d%n", book1.title, book1.authors, book1.publisher, book1.publishYear); } Book createBook(String title, String authors, String publisher, Integer publishYear){ Book book = new Book(); book.title = title; book.authors = authors; book.publisher = publisher; book.publishYear = publishYear; return book; } void printWelcomeMessage(){ System.out.println("図書館システムへようこそ."); } public static void main(String[] args){ Library lib = new Library(); lib.run(); } }
ここは確かにややこしいです.しかし,配列という古いデータ構造は,
近年のプログラム言語ではほとんど使われなくなってきています.
文法的には配列でも,実際にはより高度なデータ構造である場合がほとんどです.
Javaでも配列はデータの受け渡しにしか使われておらず,
C言語では配列を利用すべき場面では,ほとんどこのList
型に置き換わっています.
皆さんも,配列ではなく,より高度なデータ構造を使うようにしましょう.
練習問題
1. 本を追加する
以下のプログラムを参考に,shelf
に5冊の本を格納してください.
import java.util.*; public class Library { void run(){ this.printWelcomeMessage(); List<Book> shelf = new ArrayList<Book>(); this.addBooks(shelf); Book book1 = shelf.get(0); this.printBook(book1); Book book2 = shelf.get(1); this.printBook(book2); Book book3 = shelf.get(2); this.printBook(book3); Book book4 = shelf.get(3); this.printBook(book4); Book book5 = shelf.get(4); this.printBook(book5); } void printBook(Book book){ System.out.printf("%s (%s) %s, %d%n", book.title, book.authors, book.publisher, book.publishYear); } void addBooks(List<Book> shelf){ Book book1 = this.createBook("羅生門", "芥川龍之介", "青空文庫", 1997); shelf.add(book1); // 適当に本を追加する. } Book createBook(String title, String authors, String publisher, Integer publishYear){ Book book = new Book(); book.title = title; book.authors = authors; book.publisher = publisher; book.publishYear = publishYear; return book; } void printWelcomeMessage(){ System.out.println("図書館システムへようこそ."); } public static void main(String[] args){ Library lib = new Library(); lib.run(); } }
2. Listの範囲を超えてアクセスする
練習問題1では,shelf
に入れたBookの実体を取り出しています.
これをfor文での繰り返しを用いたプログラムに変更してみましょう.
List
型に入っている要素の数を取得するには,
size
メソッドが利用できます.次のプログラムで確認してみてください.
List<Book> shelf = new ArrayList<Book>(); // .... for(Integer i = 0; i < shelf.size(); i++){ // ここに,shelf からBook型の要素を取り出し, // printBook を呼び出す処理を書く. } // ...
以上のことができれば,shelf
に入れた要素の数を超えてアクセスしようとすると何が起こるでしょうか.
確認してみましょう.
このように,どのようなエラーがどんな状況で起こるのかを予め知っておくと, 今後,同じようなエラーが発生した時に,役立ちますので,様々なエラーを積極的に起こしてみましょう.
3. ファイルからBookを作成する
books.csv
に書かれた本を
shelf
に登録してみましょう.
そのために,LibraryUtil.java
を用意しました.
このプログラムをダウンロードし,Library.java
と同じディレクトリに置いてください.
void addBooks(List<Book> shelf){ LibraryUtil util = new LibraryUtil(); List<Book> books = util.readFromFile("books.csv"); for(Integer i = 0; i < books.size(); i++){ shelf.add(books.get(i)); } }
左のプログラムのように,readFromFile
メソッドに
文字列 "books.csv"
を渡すと,Book
が格納された
List
が返されます.
このBook
は books.csv に書かれている内容を読んで作成されています.
book.csv には,1行に1つのBook
の情報が記入されているとしています.
タイトル,著者名,出版社,出版年を,それぞれコンマ区切り(半角のコンマ)
で記入するとそれらが読み込まれるようになっています.
一つ一つのBook
を出力して確認してみましょう.
また,図書館システムのため,それぞれをshelf
に登録してみましょう.
4. 本の一覧を作成する
books.csv
に追記し,10冊の本を登録できるようにしましょう.
1行に一冊の本を書いていきましょう.
本日のまとめ
今日,学んだ内容は次の通りです.
- ファイル名とクラス名の関係.
- 最初に実行されるメソッド.
- 画面への出力方法.
- 型の作成方法.
- 型の実体の作成方法.
- 集合を扱う型.