5月31日

URLの構成

http://hostname:port/contextpath/path?querystring

Spring Boot で Hello World

HeloController.java

package jp.abc;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HeloController {

	@RequestMapping("/")
	public String index() {
		return "Hello Spring Boot World!";
	}
}

@RequestMappingアノテーションでいろいろなURLパスに対応する機能を追加できる。

HeloController.java に以下のメソッドを追加する。
http://localhost:8080/hello

	@RequestMapping("/hello")
	public String hello() {
		return "Hello World! - path = /hello";
	}

パス変数

URLパスに変数を含めることができる。
@PathVariable アノテーションを使用する。

http://localhost:8080/sum/123

	@RequestMapping("/sum/{num}")
	public String sum(@PathVariable int num) {
		int res = 0;
		for (int i = 1; i <= num; i++) {
			res += i;
		}
		return "total: " + res;
	}

JSONを出力する

テキストではDataObjectクラスを同一ソース内に書いているが好ましくないので、別のクラスとして新規作成する。

package jp.abc;

public class DataObject {
	private int id;
	private String name;
	private String value;

	public DataObject(int id, String name, String value) {
		this.id = id;
		this.name = name;
		this.value = value;
	}

	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getValue() {
		return value;
	}
	public void setValue(String value) {
		this.value = value;
	}
}

HeloControllerに以下のフィールドとメソッドを追加する。

	String[] names = { "tsuyano", "hanako", "taro",
					   "sachiko", "ichiro" };
	String[] mails = {
			"syoda@tsuyano.com",
			"hanako@flower",
			"taro@yamada",
			"sachiko@happy",
			"ichiro@baseball"
	};

	@RequestMapping("/users/{id}")
	public DataObject users(@PathVariable int id) {
		return new DataObject(id, names[id], mails[id]);
	}

Thymeleafを使ってWebサイトを作る

Thymeleafを使うには、ライブラリを追加する必要がある。
pom.xml に設定を追加する。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>jp.abc</groupId>
	<artifactId>MyBootApp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>MyBootApp</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

[Maven]-[プロジェクトの更新]と[実行]-[maven install]を実施する。

テキストではHeloControllerを修正しているが、ここでは新規クラスを作成する。

IndexController.java

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {

	@RequestMapping("/index")
	public String index() {
		return "index";
	}
}

HTMLファイルを作成する。
src/main/resources/templates を右クリックして新規作成でHTMLファイルを作成する。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>top page</title>
<style type="text/css">
	h1 {
		font-size: 18pt;
		font-weight: bold;
		color: gray;
	}
	body {
		font-size: 13pt;
		color: gray;
		margin: 5px 25px;
	}
</style>
</head>
<body>
<h1>helo page</h1>
<p class="msg">this is Thymeleaf sample page</p>
</body>
</html>

テンプレートに値を表示する

<body>

<h1>helo page</h1>
<p class="msg">this is Thymeleaf sample page</p>
<p class="msg" th:text="${msg}"></p>

</body>

コントローラでは引数にModelを追加する。
Modelを通してテンプレートに値を渡す。

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class IndexController {

	@RequestMapping("/index/{num}")
	public String index(@PathVariable int num, Model model) {
		int res = 0;
		for (int i = 1; i <= num; i++) {
			res += i;
		}
		model.addAttribute("msg", "total: " + res);
		return "index";
	}
}

5月28日

SpringBoot2によるWebアプリケーション開発

Eclipse に STS プラグインをインストールする。(テキストp.14)

新規プロジェクトを作成する。
プロジェクトの種類は「Spring スタータープロジェクト」(テキストp.98)

「次へ」をクリックするとツリーが表示されるので、[Web]-[Web]にチェックする。
「次へ」をクリックし「完了」をクリック。
使用しているライブラリなどのダウンロードが始まる。

ダウンロードが完了したら、プロジェクトを右クリックして[Maven]-[プロジェクトの更新]を選択する。
処理が完了したら、プロジェクトを右クリックして[実行]-[Maven install]を選択する。
不足しているライブラリなどがあればダウンロードしてくれる。

コンソールに「BUILD SUCCESS」が出ればOK。
「BUILD FAILURE」が出たら[実行]-[Maven install]をやり直す。

Webアプリケーションの実行

プロジェクトを右クリックして[実行]-[SpringBootアプリケーション]を選択する。
Webアプリケーションが起動するメッセージがコンソールに表示される。

ブラウザで「http://localhost:8080/」にアクセスするとエラーページが表示される。

5月24日

前回は、以下のテストを書いたところで終了した。。

	@Test
	void ジュースを3種類管理できる() {
		VendingMachine vm = new VendingMachine();
		vm.put(100);
		boolean b = vm.canBuy("水");
		assertThat(b, is(true));
		b = vm.canBuy("コーラ");
		assertThat(b, is(false));
		b = vm.canBuy("レッドブル");
		assertThat(b, is(false));
	}

テストを実行してエラーになることを確認する。
水を購入できるか問い合わせたところでエラーが発生。

canBuy() メソッドでは、引数で名前をもらっているけど参照していなかった。
ジュースの名前を見て購入可能かを判断するように修正する。

	public boolean canBuy(String name) {
		if (juices.size() == 0) return false;
		return total >= getJuicePrice(name);
	}

getJuicePrice()にも引数を追加して、ジュースの名前を見て価格を返すようにする。

	public int getJuicePrice(String name) {
		if (name.equals("水")) return 100;
		if (name.equals("コーラ")) return 120;
		return 200;
	}

3種類のジュースを購入できるようにしたいので、水を購入するテストを追加する。

	@Test
	void ジュースを3種類購入する() {
		VendingMachine vm = new VendingMachine();
		vm.put(1000);
		Juice j = vm.buy("水");
		assertThat(j.getName(), is("水"));
	}

これまで、1種類(コーラ)しか売ってなかった自動販売機を複数に対応させるのは割と大変。
まずは3種類のジュースを在庫できるようにする。
Mapを追加して、ジュースの名前で在庫を取得できるようにする。

public class VendingMachine {
	private int total;
	private int sales;
	private Map<String, List<Juice>> map = new HashMap<>();
	private List<Juice> juices;

	public VendingMachine() {
		total = 0;
		juices = new ArrayList<>();
		map.put("水", juices);
		for (int i = 0; i < 5; i++) {
			juices.add(new Juice("水", 100));
		}
		juices = new ArrayList<>();
		map.put("コーラ", juices);
		for (int i = 0; i < 5; i++) {
			juices.add(new Juice("コーラ", 120));
		}
		juices = new ArrayList<>();
		map.put("レッドブル", juices);
		for (int i = 0; i < 5; i++) {
			juices.add(new Juice("レッドブル", 200));
		}
	}

テストを実行すると、これまで動いていたテスト「ジュースを購入する()」などでエラーになる。
これはジュースの名前を見ていないのが原因。
ジュースの名前を見て、Mapから在庫を取得するように変更する。

	public Juice buy(String name) {
		if (!canBuy(name)) return null;
		juices = map.get(name);
		Juice j = juices.remove(0);
		total -= j.getPrice();
		sales += j.getPrice();
		return j;
	}

3種類のジュースを購入するテストを書く。

	@Test
	void ジュースを3種類購入する() {
		VendingMachine vm = new VendingMachine();
		vm.put(1000);
		Juice j = vm.buy("水");
		assertThat(j.getName(), is("水"));
		int total = vm.getTotal();
		assertThat(total, is(900));
		int stock = vm.getJuiceStock("水");
		assertThat(stock, is(4));

		j = vm.buy("レッドブル");
		assertThat(j.getName(), is("レッドブル"));
		total = vm.getTotal();
		assertThat(total, is(700));

		j = vm.buy("コーラ");
		assertThat(j.getName(), is("コーラ"));
		total = vm.getTotal();
		assertThat(total, is(580));

		int sales = vm.getSales();
		assertThat(sales, is(420));
	}

getJuiceStock() に引数を追加して、ジュースの名前を指定して在庫を取得できるようにする。

	public int getJuiceStock(String name) {
		return juices.size();
	}

ジュースの名前を見てないのに、getJuiceStock() のテストをパスしているので、怪しい実装を修正するためにテストを追加する。

	@Test
	void ジュースを3種類購入する() {
		VendingMachine vm = new VendingMachine();
		vm.put(1000);
		Juice j = vm.buy("水");
		assertThat(j.getName(), is("水"));
		int total = vm.getTotal();
		assertThat(total, is(900));
		int stock = vm.getJuiceStock("水");
		assertThat(stock, is(4));
		stock = vm.getJuiceStock("レッドブル");
		assertThat(stock, is(5));

		j = vm.buy("レッドブル");
		assertThat(j.getName(), is("レッドブル"));
		total = vm.getTotal();
		assertThat(total, is(700));

		j = vm.buy("コーラ");
		assertThat(j.getName(), is("コーラ"));
		total = vm.getTotal();
		assertThat(total, is(580));

		int sales = vm.getSales();
		assertThat(sales, is(420));
	}

このテストで、レッドブルの在庫数を正しく返せてないことがわかる。
getJuiceStocck() をきちんと実装しなおす。

	public int getJuiceStock(String name) {
		juices = map.get(name);
		return juices.size();
	}

購入可能なドリンクのリストを知りたい!
テストを作成する。

	@Test
	void 購入可能なドリンクのリスト() {
		VendingMachine vm = new VendingMachine();
		Juice[] list = vm.getBuyableJuices();
		assertThat(list.length, is(0));
	}

コンパイルエラーを解消する。
VendingMachine に以下のメソッドを追加する。

	public Juice[] getBuyableJuices() {
		return null;
	}

テストには失敗するので、テストにパスするように実装する。
とりあえず fake で。

	public Juice[] getBuyableJuices() {
		return new Juice[0];
	}

テストを追加する。
100円を投入すると水を購入できる。

	@Test
	void 購入可能なドリンクのリスト() {
		VendingMachine vm = new VendingMachine();
		Juice[] list = vm.getBuyableJuices();
		assertThat(list.length, is(0));
		vm.put(100);
		list = vm.getBuyableJuices();
		assertThat(list.length, is(1));
		assertThat(list[0].getName(), is("水"));
	}

100円を追加投入すると3種類のドリンクを購入できる。

	@Test
	void 購入可能なドリンクのリスト() {
		VendingMachine vm = new VendingMachine();
		Juice[] list = vm.getBuyableJuices();
		assertThat(list.length, is(0));
		vm.put(100);
		list = vm.getBuyableJuices();
		assertThat(list.length, is(1));
		assertThat(list[0].getName(), is("水"));
		vm.put(100);
		list = vm.getBuyableJuices();
		assertThat(list.length, is(3));
	}

手抜きの実装でテストをパスさせる。

	public Juice[] getBuyableJuices() {
		if (total >= 200) {
			Juice[] list = {
					new Juice("水", 100),
					new Juice("コーラ", 120),
					new Juice("レッドブル", 200)
				};
			return list;
		}
		if (total >= 120) {
			Juice[] list = {
					new Juice("水", 100),
					new Juice("コーラ", 120),
				};
			return list;

		}
		if (total >= 100) {
			Juice[] list = {
					new Juice("水", 100),
				};
			return list;

		}
		return new Juice[0];
	}

在庫がなくなったらリストに出ないテストを作成する。
合計700円を投入して水を5個買うと、200円残る。
水の在庫がなくなったので、リストはコーラとレッドブルの2個だけになる。

	@Test
	void 購入可能なドリンクのリスト() {
		VendingMachine vm = new VendingMachine();
		Juice[] list = vm.getBuyableJuices();
		assertThat(list.length, is(0));
		vm.put(100);
		list = vm.getBuyableJuices();
		assertThat(list.length, is(1));
		assertThat(list[0].getName(), is("水"));
		vm.put(100);
		list = vm.getBuyableJuices();
		assertThat(list.length, is(3));
		vm.put(500);
		for (int i = 0; i < 5; i++) {
			Juice j = vm.buy("水");
			assertThat(j.getName(), is("水"));
		}
		list = vm.getBuyableJuices();
		assertThat(list.length, is(2));
	}
	public Juice[] getBuyableJuices() {
		Collection<List<Juice>> c = map.values();
		List<Juice> list = new ArrayList<>();
		for (List<Juice> l : c) {
			if (l.size() == 0) continue;
			Juice j = l.get(0);
			if (j.getPrice() > total) continue;
			list.add(j);
		}
		return list.toArray(new Juice[0]);
	}

5月21日

前回は返金するテストを作ったところで終わっていたので、まずはテストを実行してみる。
すると、返金するテストでエラーになるので、そこから作業を再開すればいいことがわかる。

返金を実装する。

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}

扱えないお金

テストを書く。

	@Test
	void 投入できないお金() {
		VendingMachine vm = new VendingMachine();
		vm.put(1);
		int total = vm.getTotal();
		assertThat(total, is(0));
		vm.put(5);
		total = vm.getTotal();
		assertThat(total, is(0));
		vm.put(20);
		total = vm.getTotal();
		assertThat(total, is(0));
		vm.put(5000);
		total = vm.getTotal();
		assertThat(total, is(0));
	}

10円・50円・100円・500円・1000円だけ受け付けるようにすればよい。

	public void put(int i) {
		// 10円・50円・100円・500円・1000円だけ受け付ける
		if (i == 10 ||
			i == 50 ||
			i == 100 ||
			i == 500 ||
			i == 1000) {
			total += i;
		}
	}

ジュースの管理

テストを書く。

	@Test
	void ジュースを格納できる() {
		VendingMachine vm = new VendingMachine();
		String name = vm.getJuiceName();
		assertThat(name, is("コーラ"));
		int price = vm.getJuicePrice();
		assertThat(price, is(120));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(5));
	}

このテストに関しては、fakeだけでテストにパスできる。

	public String getJuiceName() {
		return "コーラ";
	}

	public int getJuicePrice() {
		return 120;
	}

	public int getJuiceStock() {
		return 5;
	}

ジュースを購入する。

テストを書く。

	@Test
	void ジュースを購入する() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.canBuy("コーラ");
		assertThat(b, is(false));
		vm.put(500);
		b = vm.canBuy("コーラ");
		assertThat(b, is(true));
	}

テストにパスするように自動販売機を実装する。

	public boolean canBuy(String name) {
		return total >= getJuicePrice();
	}

購入できるようにする。

	@Test
	void ジュースを購入する() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.canBuy("コーラ");
		assertThat(b, is(false));
		vm.put(500);
		b = vm.canBuy("コーラ");
		assertThat(b, is(true));
		Juice juice = vm.buy("コーラ");

Juiceがないので新規にクラスを作成する。

package jp.abc;

public class Juice {
	private String name;
	private int price;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
}

Juiceを作ったので、テストの続きを作成する。

	@Test
	void ジュースを購入する() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.canBuy("コーラ");
		assertThat(b, is(false));
		vm.put(500);
		b = vm.canBuy("コーラ");
		assertThat(b, is(true));
		Juice juice = vm.buy("コーラ");
		assertThat(juice.getName(), is("コーラ"));
	}

テストではジュースの名前だけチェックしているのでfakeで実装しておく。

	public Juice buy(String name) {
		Juice j = new Juice();
		j.setName("コーラ");
		return j;
	}

ジュースを購入すると在庫を減らさないといけないのでテストを追加する。

	@Test
	void ジュースを購入する() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.canBuy("コーラ");
		assertThat(b, is(false));
		vm.put(500);
		b = vm.canBuy("コーラ");
		assertThat(b, is(true));
		Juice juice = vm.buy("コーラ");
		assertThat(juice.getName(), is("コーラ"));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(4));
	}

テストをパスするようにまじめに実装する。
Juiceクラスには、引数付きコンストラクタが欲しいので追加しておく。
既存のコードで引数なしのコンストラクタを使っているので引数なしのコンストラクタも定義しておく。

	public Juice(String name, int price) {
		this.name = name;
		this.price = price;
	}
	public Juice() {
		this("", 0);
	}
package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class VendingMachine {
	private int total;
	private List<Juice> juices = new ArrayList<>();

	public VendingMachine() {
		total = 0;
		for (int i = 0; i < 5; i++) {
			juices.add(new Juice("コーラ", 120));
		}
	}

	public void put(int i) {
		// 10円・50円・100円・500円・1000円だけ受け付ける
		if (i == 10 ||
			i == 50 ||
			i == 100 ||
			i == 500 ||
			i == 1000) {
			total += i;
		}
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}

	public String getJuiceName() {
		return "コーラ";
	}

	public int getJuicePrice() {
		return 120;
	}

	public int getJuiceStock() {
		return juices.size();
	}

	public boolean canBuy(String name) {
		return total >= getJuicePrice();
	}

	public Juice buy(String name) {
		Juice j = juices.remove(0);
		return j;
	}
}

ジュースを購入すると合計金額が減るのをテストに追加する。

	@Test
	void ジュースを購入する() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.canBuy("コーラ");
		assertThat(b, is(false));
		vm.put(500);
		b = vm.canBuy("コーラ");
		assertThat(b, is(true));
		Juice juice = vm.buy("コーラ");
		assertThat(juice.getName(), is("コーラ"));
		int stock = vm.getJuiceStock();
		assertThat(stock, is(4));
		int total = vm.getTotal();
		assertThat(total, is(380));
	}

テストにパスするように実装する。

	public Juice buy(String name) {
		Juice j = juices.remove(0);
		total -= j.getPrice();
		return j;
	}

投入金額が足りないときはジュースを購入できないテストを追加する。

	@Test
	void 金額不足でジュースを購入できない() {
		VendingMachine vm = new VendingMachine();
		Juice j = vm.buy("コーラ");
		assertThat(j, is(nullValue()));
	}

テストにパスするように実装する。

	public Juice buy(String name) {
		if (!canBuy(name)) return null;
		Juice j = juices.remove(0);
		total -= j.getPrice();
		return j;
	}

在庫がないときはジュースを購入できないテストを追加する。

	@Test
	void 在庫がないのでジュースを購入できない() {
		VendingMachine vm = new VendingMachine();
		vm.put(1000);
		for (int i = 0; i < 5; i++) {
			Juice j = vm.buy("コーラ");
			assertThat(j.getName(), is("コーラ"));
		}
		Juice j = vm.buy("コーラ");
		assertThat(j, is(nullValue()));
	}

テストにパスするように実装する。
在庫が0なので購入できない。

	public boolean canBuy(String name) {
		if (juices.size() == 0) return false;
		return total >= getJuicePrice();
	}

売上金額を取得できるようにする。
まずはテストを作成する。

	@Test
	void 現在の売上金額を取得できる() {
		VendingMachine vm = new VendingMachine();
		int sales = vm.getSales();
		assertThat(sales, is(0));
		vm.put(1000);
		Juice j = vm.buy("コーラ");
		sales = vm.getSales();
		assertThat(sales, is(120));
	}

テストにパスするように実装する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class VendingMachine {
	private int total;
	private int sales;
	private List<Juice> juices = new ArrayList<>();

	public VendingMachine() {
		total = 0;
		for (int i = 0; i < 5; i++) {
			juices.add(new Juice("コーラ", 120));
		}
	}

	public void put(int i) {
		// 10円・50円・100円・500円・1000円だけ受け付ける
		if (i == 10 ||
			i == 50 ||
			i == 100 ||
			i == 500 ||
			i == 1000) {
			total += i;
		}
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		int refund = total;
		total = 0;
		return refund;
	}

	public String getJuiceName() {
		return "コーラ";
	}

	public int getJuicePrice() {
		return 120;
	}

	public int getJuiceStock() {
		return juices.size();
	}

	public boolean canBuy(String name) {
		if (juices.size() == 0) return false;
		return total >= getJuicePrice();
	}

	public Juice buy(String name) {
		if (!canBuy(name)) return null;
		Juice j = juices.remove(0);
		total -= j.getPrice();
		sales += j.getPrice();
		return j;
	}

	public int getSales() {
		return sales;
	}
}

返金操作で釣銭を返すテストを作成する。

	@Test
	void 返金操作で釣銭を返す() {
		VendingMachine vm = new VendingMachine();
		vm.put(1000);
		Juice j = vm.buy("コーラ");
		int refund = vm.refund();
		assertThat(refund, is(880));
	}

何も実装しないでもテストにパスした!!!!!!!

機能拡張する。

ジュースを3種類管理できるようにする。

	@Test
	void ジュースを3種類管理できる() {
		VendingMachine vm = new VendingMachine();
		vm.put(100);
		boolean b = vm.canBuy("水");
		assertThat(b, is(true));
		b = vm.canBuy("コーラ");
		assertThat(b, is(false));
		b = vm.canBuy("レッドブル");
		assertThat(b, is(false));
	}

5月17日

gitを使用したソースコードのバージョン管理

プロジェクトを右クリックして[チーム]-[プロジェクトの共用] を選択する。
GitとSVNがあるのでGitを選択して「次へ」をクリックする。
リポジトリーの「作成」をクリック。
適当なフォルダを選択して「完了」をクリック。

.gitignore ファイルを設定する。

.gitignoreファイルには、Gitの管理対象外にするファイルを記述する。
例えばクラスファイルなどはコンパイルで自動生成されるので、管理不要である。

.gitignoreファイルを表示するには、パッケージエクスプローラの右にある「▽」をクリックする。
メニューで「フィルター」を選択して「.*リソース」のチェックを外す。

/bin/
/.settings
.project
.classpath
*.class
*.jar

Gitにファイルを登録することを「コミット」と呼ぶ。

自動販売機の実装に戻る。

前回、テストがエラーになる状態で終わっていたので、テストを実行してみる。
10円を投入して投入金額の合計を取得すると0円なのでエラーになっている。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class VendingMachineTest {

	@Test
	void お金を投入する() {
		VendingMachine vm = new VendingMachine();
		vm.put(10);
		int total = vm.getTotal();
		assertThat(total, is(10));
	}

}

fakeでgetTotal()で10を返すようにすればテストにパスする。

package jp.abc;

public class VendingMachine {

	public void put(int i) {

	}

	public int getTotal() {
		return 10;
	}

}

テストを追加する。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class VendingMachineTest {

	@Test
	void お金を投入する() {
		VendingMachine vm = new VendingMachine();
		vm.put(10);
		int total = vm.getTotal();
		assertThat(total, is(10));
		vm.put(100);
		total = vm.getTotal();
		assertThat(total, is(110));
	}

}

テストにパスするように実装を行う。

テストにパスしたタイミングでソースコードをGitに登録する。
登録することをコミットという。
プロジェクトを右クリックし、[チーム]-[コミット]を選択する。
コミットメッセージを入力する。
登録したいファイルがステージングにあることを確認して[コミット]をクリックする。

以下、投入できないお金と返金を追加したテスト。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class VendingMachineTest {

	@Test
	void お金を投入する() {
		VendingMachine vm = new VendingMachine();
		vm.put(10);
		int total = vm.getTotal();
		assertThat(total, is(10));
		vm.put(100);
		total = vm.getTotal();
		assertThat(total, is(110));
	}

	@Test
	void 投入できないお金() {
		VendingMachine vm = new VendingMachine();
		vm.put(1);
		int total = vm.getTotal();
		assertThat(total, is(0));
	}

	@Test
	void 返金する() {
		VendingMachine vm = new VendingMachine();
		vm.put(1000);
		int total = vm.getTotal();
		assertThat(total, is(1000));
		int refund = vm.refund();
		total = vm.getTotal();
		assertThat(total, is(0));
		assertThat(refund, is(1000));
	}
}

1円を投入されても合計金額に加算しない。

package jp.abc;

public class VendingMachine {
	private int total;

	public VendingMachine() {
		total = 0;
	}

	public void put(int i) {
		if (i == 1) return;
		total += i;
	}

	public int getTotal() {
		return total;
	}

	public int refund() {
		return 0;
	}

}

5月14日

テストを実行するとMusicPlayerTestの「プレイリストに音楽を追加する()」でエラーになるので、そこから作業を再開すればよい。

MusicPlayer#addMusicToPlayList()を実装する。

	public void addMusicToPlayList(Music m, PlayList p) {
		p.addMusic(m);
	}

PlayList#addMucis()のコンパイルエラーを解消し、テストにパスさせる。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class PlayList {
	private String name;
	private List<Music> musics = new ArrayList<>();

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int countMusic() {
		return musics.size();
	}

	public void addMusic(Music m) {
		musics.add(m);
	}

}

音楽を再生する機能を追加する。
ここでは、再生した順番にMusicを格納したListを返す playAll() を作る。

	@Test
	void 音楽を再生する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		mp.addMusic("flamingo", "米津玄師");
		List<Music> list = mp.playAll();
		assertThat(list.get(0).getTitle(), is("Lemon"));
		assertThat(list.get(1).getTitle(), is("flamingo"));
	}

MusicPlayer#playAll() を再生する。

	public List<Music> playAll() {
		return null;
	}

テストを実行すると、NullPointerExceptionが発生する。

MusicPlayer#playAll() を修正する。

	public List<Music> playAll() {
		return musics;
	}

シャッフル再生機能を追加する。
テストを作成する。
ランダムな結果に対するテストを書くのは難しいので、テストでは曲数だけ確認してコンソール出力を目視で確認する。

	@Test
	void 音楽をシャッフル再生する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		mp.addMusic("flamingo", "米津玄師");
		mp.addMusic("紅蓮華", "LISA");
		mp.addMusic("日本のコメは世界一", "打首獄門同好会");
		mp.addMusic("Killing me", "SIM");
		List<Music> list = mp.playShuffle();
		assertThat(list.size(), is(5));
	}

MusicPayer#playShuffle() を作成する。
とりあえず、シャッフルしないままのmusicsを返しておく。
Musicを追加した順番通りにコンソール出力される。

	public List<Music> playShuffle() {
		// shuffle
		List<Music> list = musics;
		for (Music m : list) {
			System.out.println(m);
		}
		return musics;
	}

Music#toString()を追加してタイトルとアーティストを表示するようにしておく。

	@Override
	public String toString() {
		return "Music [title=" + title + ", artist=" + artist + "]";
	}

シャッフル再生でもとの曲順が変わるのはまずいので、テストを追加しておく。

	@Test
	void 音楽をシャッフル再生する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		mp.addMusic("flamingo", "米津玄師");
		mp.addMusic("紅蓮華", "LISA");
		mp.addMusic("日本のコメは世界一", "打首獄門同好会");
		mp.addMusic("Killing me", "SIM");
		List<Music> list = mp.playShuffle();
		assertThat(list.size(), is(5));
		// シャッフル後も曲順が変わってないことを確認
		assertThat(list.get(0).getTitle(), is("Lemon"));
		assertThat(list.get(1).getTitle(), is("flamingo"));
	}

MusicPlayerを実装する。

	public List<Music> playShuffle() {
		// shuffle
		List<Music> list = new ArrayList<>(musics);
		Collections.shuffle(list);
		for (Music m : list) {
			System.out.println(m);
		}
		return musics;
	}

別の例題に挑戦してみよう!

TDD Boot Camp 大阪 2.0/課題( #tddbc )

飲み物自動販売機を作ってみる。

新規プロジェクト: vendingmachine
新規クラス: jp.abc.VendingMachine
新規JUnitテストケース: jp.abc.VendingMachineTest

package jp.abc;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class VendingMachineTest {

	@Test
	void test() {
		fail("まだ実装されていません");
	}

}

まずは次の課題を実装してみる。
・お金を投入できるようにする。
・投入した金額の総計を取得できる。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class VendingMachineTest {

	@Test
	void お金を投入する() {
		VendingMachine vm = new VendingMachine();
		vm.put(10);
		int total = vm.getTotal();
		assertThat(total, is(10));
	}

}

Eclipseにメソッドを生成してもらってコンパイルエラーを解消する。

package jp.abc;

public class VendingMachine {

	public void put(int i) {
		// TODO 自動生成されたメソッド・スタブ

	}

	public int getTotal() {
		// TODO 自動生成されたメソッド・スタブ
		return 0;
	}

}

Gitでソースコードのバージョン管理する

プロジェクトを右クリックして[チーム]-[プロジェクトの共用] を選択する。
GitとSVNがあるのでGitを選択して「次へ」をクリックする。
リポジトリーの「作成」をクリック。
適当なフォルダを選択して「完了」をクリック。

5月10日

コレクションフレームワークの練習として、音楽プレーヤーみたいなアプリを作ってみる。

アプリを作成するために、新規Javaプロジェクトとして「musicplayer」プロジェクトを作成する。

新規クラスで「MusicPlayer」を作成する。
このとき、パッケージ名は「jp.abc」とする。
一般的にパッケージ名を決める場合は、企業やアプリのドメイン名を反転したものを使用する。
そうしておけば、世界的に重複する心配がないので。

例:
twitter.com → com.twitter.Hoge
gmail.google.com → com.google.gmail

package jp.abc;

public class MusicPlayer {

}

テストクラスを作成する。
[新規]-[その他] から「JUnitテストケース」を選択する。

package jp.abc;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
	}

}

addMusic()上にマウスカーソルをもっていき、「addMusic(String, String)を作成する」をクリックすると、
MusicPlayerにメソッドが追加される。
引数名が不適切なので、修正する。

package jp.abc;

public class MusicPlayer {

	public void addMusic(String title, String artist) {

	}

}

音楽を追加した後の動作を考える。
とりあえず、音楽の数を調べたいので、テストケースにメソッドを追加する。

package jp.abc;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		int n = mp.countMusic();
	}

}

countMusic()がコンパイルエラーなので、マウスカーソルをその上に移動し、「countMusic()を作成する」をクリックし、メソッドを作成する。

package jp.abc;

public class MusicPlayer {

	public void addMusic(String title, String artist) {

	}

	public int countMusic() {
		return 0;
	}

}

テストを追加する。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		int n = mp.countMusic();
		assertThat(n, is(1));
		mp.addMusic("flamingo", "米津玄師");
		n = mp.countMusic();
		assertThat(n, is(2));
	}

}

テストを実行するとエラーになるので、MusicPlayerをそれなりに実装する必要性が発生する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class MusicPlayer {
	private List<Music> musics = new ArrayList<>();

	public void addMusic(String title, String artist) {
		Music m = new Music();
		musics.add(m);
	}

	public int countMusic() {
		return musics.size();
	}

}

テストをパスできたので、新しい機能を追加する。
そのために、テストを追加する。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		int n = mp.countMusic();
		assertThat(n, is(1));
		mp.addMusic("flamingo", "米津玄師");
		n = mp.countMusic();
		assertThat(n, is(2));
	}

	@Test
	void タイトルとアーティストの確認() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		Music m = mp.getMusic(0);
	}
}

getMusic()でコンパイルエラーになっているので、マウスカーソルを上に持っていき「getMusic()を作成する」をクリックし、メソッドを作成する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class MusicPlayer {
	private List<Music> musics = new ArrayList<>();

	public void addMusic(String title, String artist) {
		Music m = new Music();
		musics.add(m);
	}

	public int countMusic() {
		return musics.size();
	}

	public Music getMusic(int i) {
		// TODO 自動生成されたメソッド・スタブ
		return null;
	}

}

テストを追加する。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		int n = mp.countMusic();
		assertThat(n, is(1));
		mp.addMusic("flamingo", "米津玄師");
		n = mp.countMusic();
		assertThat(n, is(2));
	}

	@Test
	void タイトルとアーティストの確認() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		Music m = mp.getMusic(0);
		assertNotNull(m);
	}
}

テストがエラーになるので、テストにパスするようにMusicPlayerを変更する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class MusicPlayer {
	private List<Music> musics = new ArrayList<>();

	public void addMusic(String title, String artist) {
		Music m = new Music();
		musics.add(m);
	}

	public int countMusic() {
		return musics.size();
	}

	public Music getMusic(int i) {
		return new Music();
	}

}

テストを追加する。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		int n = mp.countMusic();
		assertThat(n, is(1));
		mp.addMusic("flamingo", "米津玄師");
		n = mp.countMusic();
		assertThat(n, is(2));
	}

	@Test
	void タイトルとアーティストの確認() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		Music m = mp.getMusic(0);
		assertNotNull(m);
		assertThat(m.getTitle(), is("Lemon"));
	}
}

getTitle()のコンパイルエラーを解消する。

package jp.abc;

public class Music {

	public String getTitle() {
		// TODO 自動生成されたメソッド・スタブ
		return null;
	}

}

コンパイルエラーが解消したけどテストはエラーなので、テストにパスするように、戻り値で”Lemon”を返す。

package jp.abc;

public class Music {

	public String getTitle() {
		return "Lemon";
	}

}

さらにテストを追加する。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		int n = mp.countMusic();
		assertThat(n, is(1));
		mp.addMusic("flamingo", "米津玄師");
		n = mp.countMusic();
		assertThat(n, is(2));
	}

	@Test
	void タイトルとアーティストの確認() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		Music m = mp.getMusic(0);
		assertNotNull(m);
		assertThat(m.getTitle(), is("Lemon"));
		mp.addMusic("flamingo", "米津玄師");
		m = mp.getMusic(1);
		assertNotNull(m);
		assertThat(m.getTitle(), is("flamingo"));
	}
}

きちんと実装しないとテストにパスしないっぽい。。。
Musicクラスにタイトルとアーティストを保存できるよう、最小限の実装をする。

package jp.abc;

public class Music {
	private String title;
	private String artist;

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getArtist() {
		return artist;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}

}

MusicPlayerも、addMusic()とgetMusoic()をきちんと実装する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class MusicPlayer {
	private List<Music> musics = new ArrayList<>();

	public void addMusic(String title, String artist) {
		Music m = new Music();
		m.setTitle(title);
		m.setArtist(artist);
		musics.add(m);
	}

	public int countMusic() {
		return musics.size();
	}

	public Music getMusic(int i) {
		return musics.get(i);
	}

}

これでテストにパスできるようになった!

音楽プレーヤーにプレイリストを追加できるようにする。
テストを追加する。

package jp.abc;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class MusicPlayerTest {

	@Test
	void 音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		int n = mp.countMusic();
		assertThat(n, is(1));
		mp.addMusic("flamingo", "米津玄師");
		n = mp.countMusic();
		assertThat(n, is(2));
	}

	@Test
	void タイトルとアーティストの確認() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		Music m = mp.getMusic(0);
		assertNotNull(m);
		assertThat(m.getTitle(), is("Lemon"));
		mp.addMusic("flamingo", "米津玄師");
		m = mp.getMusic(1);
		assertNotNull(m);
		assertThat(m.getTitle(), is("flamingo"));
	}
	
	@Test
	void プレイリストを作る() {
		MusicPlayer mp = new MusicPlayer();
		mp.addPlayList("お気に入り");
	}
}

MusicPlayerにaddPlayList()を追加してコンパイルエラーを解消する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class MusicPlayer {
	private List<Music> musics = new ArrayList<>();

	public void addMusic(String title, String artist) {
		Music m = new Music();
		m.setTitle(title);
		m.setArtist(artist);
		musics.add(m);
	}

	public int countMusic() {
		return musics.size();
	}

	public Music getMusic(int i) {
		return musics.get(i);
	}

	public void addPlayList(String name) {

	}

}

テストを追加する。
Musicと同様に、プレイリストの数を返す機能を追加してみる。

	@Test
	void プレイリストを作る() {
		MusicPlayer mp = new MusicPlayer();
		mp.addPlayList("お気に入り");
		int n = mp.countPlayList();
		assertThat(n, is(1));
	}

countPlayList()を作成してコンパイルエラーを解消する。
「return 1;」でテストにパスする。

	public int countPlayList() {
		return 1;
	}

さらにプレイリストを追加する。
テストがエラーになるので、パスするようにMusicPlayerを実装する。

	@Test
	void プレイリストを作る() {
		MusicPlayer mp = new MusicPlayer();
		mp.addPlayList("お気に入り");
		int n = mp.countPlayList();
		assertThat(n, is(1));
		mp.addPlayList("バラード集");
		n = mp.countPlayList();
		assertThat(n, is(2));
	}

addMusic()とcountMusic()を参考にして実装する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class MusicPlayer {
	private List<Music> musics = new ArrayList<>();
	private List<PlayList> playLists = new ArrayList<>();

	public void addMusic(String title, String artist) {
		Music m = new Music();
		m.setTitle(title);
		m.setArtist(artist);
		musics.add(m);
	}

	public int countMusic() {
		return musics.size();
	}

	public Music getMusic(int i) {
		return musics.get(i);
	}

	public void addPlayList(String name) {
		playLists.add(new PlayList());
	}

	public int countPlayList() {
		return playLists.size();
	}

}

プレイリストを取得するテストを追加する。

	@Test
	void プレイリスト名を調べる() {
		MusicPlayer mp = new MusicPlayer();
		mp.addPlayList("お気に入り");
		PlayList p = mp.getPlayList(0);
		assertNotNull(p);
	}

getPlayList()のコンパイルエラーを解消する。
MusicPlayerにメソッドを追加する。

	public PlayList getPlayList(int i) {
		return null;
	}

テストがエラーになるので、fakeでテストにパスさせる。

	public PlayList getPlayList(int i) {
		return new PlayList();
	}

テストを追加する。

	@Test
	void プレイリスト名を調べる() {
		MusicPlayer mp = new MusicPlayer();
		mp.addPlayList("お気に入り");
		PlayList p = mp.getPlayList(0);
		assertNotNull(p);
		String s = p.getName();
		assertThat(s, is("お気に入り"));
	}

getName() のコンパイルエラーを解消する。

package jp.abc;

public class PlayList {

	public String getName() {
		return null;
	}

}

テストがエラーになるので、fakeでテストにパスさせる。

package jp.abc;

public class PlayList {

	public String getName() {
		return "お気に入り";
	}

}

テストを追加する。

	@Test
	void プレイリスト名を調べる() {
		MusicPlayer mp = new MusicPlayer();
		mp.addPlayList("お気に入り");
		PlayList p = mp.getPlayList(0);
		assertNotNull(p);
		String s = p.getName();
		assertThat(s, is("お気に入り"));
		mp.addPlayList("バラード集");
		p = mp.getPlayList(1);
		assertThat(p.getName(), is("バラード集"));
	}

テストにパスするようにMusicPlayerを実装する。

package jp.abc;

import java.util.ArrayList;
import java.util.List;

public class MusicPlayer {
	private List<Music> musics = new ArrayList<>();
	private List<PlayList> playLists = new ArrayList<>();

	public void addMusic(String title, String artist) {
		Music m = new Music();
		m.setTitle(title);
		m.setArtist(artist);
		musics.add(m);
	}

	public int countMusic() {
		return musics.size();
	}

	public Music getMusic(int i) {
		return musics.get(i);
	}

	public void addPlayList(String name) {
		PlayList p = new PlayList();
		p.setName(name);
		playLists.add(p);
	}

	public int countPlayList() {
		return playLists.size();
	}

	public PlayList getPlayList(int i) {
		return playLists.get(i);
	}

}

プレイリストに音楽を追加できるようにしたい!
テストを追加する。

	@Test
	void プレイリストに音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		mp.addPlayList("お気に入り");
		Music m = mp.getMusic(0);
		assertThat(m.getTitle(), is("Lemon"));
		PlayList p = mp.getPlayList(0);
		mp.addMusicToPlayList(m, p);
		int n = p.countMusic();
		assertThat(n, is(1));
	}

コンパイルエラーを解消する。

package jp.abc;

public class PlayList {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int countMusic() {
		return 0;
	}

}

fakeでテストにパスさせる。

package jp.abc;

public class PlayList {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int countMusic() {
		return 1;
	}

}

テストを追加する。

	@Test
	void プレイリストに音楽を追加する() {
		MusicPlayer mp = new MusicPlayer();
		mp.addMusic("Lemon", "米津玄師");
		mp.addMusic("flamingo", "米津玄師");
		mp.addPlayList("お気に入り");
		Music m = mp.getMusic(0);
		assertThat(m.getTitle(), is("Lemon"));
		PlayList p = mp.getPlayList(0);
		mp.addMusicToPlayList(m, p);
		int n = p.countMusic();
		assertThat(n, is(1));
		m = mp.getMusic(1);
		mp.addMusicToPlayList(m, p);
		n = p.countMusic();
		assertThat(n, is(2));
	}

TDDするときは、エラー状態でその日の作業を終える。
次に作業を再開するとき、テストを実行すればエラーが出るので、そこから再開すればいいことがすぐにわかる。

5月7日

授業の内容を記録するブログ
URL: http://2019se3.satoshis.jp/

テキストの14章から

リスト14-1

package ex;

public class Main {

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		// ここで何らかの時間がかかる処理
		long end = System.currentTimeMillis();
		System.out.println("処理にかかった時間は…"
				+ (end - start) + "ミリ秒でした");
	}

}

前回の素数かどうかを判定するためのJUnitテスト PrimeTest.java

package ex;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.jupiter.api.Test;

class PrimeTest {

	@Test
	void 素数() {
		Prime p = new Prime();
		assertThat(p.isPrime(2), is(true));
		assertThat(p.isPrime(3), is(true));
		assertThat(p.isPrime(5), is(true));
		assertThat(p.isPrime(7), is(true));
		assertThat(p.isPrime(11), is(true));
		assertThat(p.isPrime(13), is(true));
		assertThat(p.isPrime(6700417), is(true));
		assertThat(p.isPrime(Integer.MAX_VALUE), is(true));
		//System.out.println(Integer.MAX_VALUE);
	}

	@Test
	void 素数ではない() {
		Prime p = new Prime();
		assertThat(p.isPrime(4), is(false));
		assertThat(p.isPrime(6), is(false));
		assertThat(p.isPrime(8), is(false));
		assertThat(p.isPrime(9), is(false));
	}
}

ターゲットプログラム Prime.java

package ex;

public class Prime {

	public boolean isPrime(int n) {
		if (n == 2) return true;
		if (n % 2 == 0) return false;
		for (int i = 3; i < n /2; i+=2) {
			if (n % i == 0) return false;
		}
		return true;
	}

}

この素数判定をリスト14-1の時間がかかる処理として追加する。

package ex;

public class Main {

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		// ここで何らかの時間がかかる処理
		Prime p = new Prime();
		boolean b = p.isPrime(Integer.MAX_VALUE);
		System.out.println(Integer.MAX_VALUE
				+ "の素数判定結果は" + b + "です");
		long end = System.currentTimeMillis();
		System.out.println("処理にかかった時間は…"
				+ (end - start) + "ミリ秒でした");
	}

}

実行結果は以下の通り。

2147483647の素数判定結果はtrueです
処理にかかった時間は…1148ミリ秒でした

テキストのリスト14-2のコードを追加する。

package ex;

import java.util.Date;

public class Main {

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		// ここで何らかの時間がかかる処理
		Prime p = new Prime();
		boolean b = p.isPrime(Integer.MAX_VALUE);
		System.out.println(Integer.MAX_VALUE
				+ "の素数判定結果は" + b + "です");
		long end = System.currentTimeMillis();
		System.out.println("処理にかかった時間は…"
				+ (end - start) + "ミリ秒でした");

		// リスト14-2
		Date now = new Date();
		System.out.println(now);
		System.out.println(now.getTime());
		Date past = new Date(1316622225935L);
		System.out.println(past);
	}

}

実行結果は以下の通り。

2147483647の素数判定結果はtrueです
処理にかかった時間は…1142ミリ秒でした
Tue May 07 10:34:32 JST 2019
1557192872306
Thu Sep 22 01:23:45 JST 2011

テキストのリスト14-3をCalMain.javaとして作成する。

package ex;

import java.util.Calendar;
import java.util.Date;

public class CalMain {

	public static void main(String[] args) {
		// 現在の年を表示する
		Date now = new Date();
		Calendar c = Calendar.getInstance();
		c.setTime(now);
		int y = c.get(Calendar.YEAR);
		System.out.println("今年は" + y + "年です");
		// 指定した日のDate型の値を得る
		c.set(2010, 8, 22, 1, 23, 45);
		Date past = c.getTime();
		System.out.println(past);
	}

}

SimpleDateFormat の使用例としてリスト14-4 のコードをCalMain.javaに追加する。

package ex;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class CalMain {

	public static void main(String[] args) throws Exception {
		// 現在の年を表示する
		Date now = new Date();
		Calendar c = Calendar.getInstance();
		c.setTime(now);
		int y = c.get(Calendar.YEAR);
		System.out.println("今年は" + y + "年です");
		// 指定した日のDate型の値を得る
		c.set(2010, 8, 22, 1, 23, 45);
		Date past = c.getTime();
		System.out.println(past);

		// リスト14-4 のコードを追加
		SimpleDateFormat f =
				new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		String s = f.format(now);
		System.out.println(s);
		// 指定日時の文字列を解析しDate型として得る
		Date d = f.parse("2011/09/22 01:23:45");
		System.out.println(d);
	}

}

リスト14-5 Empty.java

package ex;

public class Empty {

}

EmptyMain.java (テキストでは Main.java)

package ex;

public class EmptyMain {

	public static void main(String[] args) {
		Empty e = new Empty();
		String s = e.toString();
		System.out.println(s);
	}

}

実行結果は以下の通り。
@の手前はFQCNで@のあとはハッシュ値の16進数表記。

ex.Empty@70dea4e

toString() メソッドのカスタマイズ
Heroクラスを選択した状態で、メニューから[ソース]-[toString()生成]を選択する。
そのままOKすると、EclipseがtoString()メソッドを生成してくれる。
toString()で表示する必要がないフィールドがあれば、チェックを外してからOKする。

Hero.java

package ex;

public class Hero {
	String name;
	int hp;

	@Override
	public String toString() {
		return getClass().getName()
				+ "[name=" + name + ", hp=" + hp + "]";
	}
}

HeroMain.java

package ex;

public class HeroMain {

	public static void main(String[] args) {
		Hero h = new Hero();
		h.name = "ミナト";
		h.hp = 100;
		System.out.println(h.toString());
	}

}

Heroクラスを比較するために equals() メソッドを使ってみる。

package ex;

public class HeroMain {

	public static void main(String[] args) {
		Hero h = new Hero();
		h.name = "ミナト";
		h.hp = 100;
		System.out.println(h.toString());
		Hero h2 = new Hero();
		h2.name = "ミナト";
		h2.hp = 100;
		System.out.println(h.equals(h2));
	}

}

実行結果は以下の通り。

ex.Hero[name=ミナト, hp=100]
false

名前とhpが同じならtrueにしたい。

Eclipseに equals() メソッドを生成する機能があるので、それを使うと便利。
Heroクラスを選択した状態でメニューから[ソース]-[hashCode()およびequals()の生成]を選択する。
hpとnameフィールドにチェックが入っているのを確認してOKする。
すると、hashCode()とequals()メソッドが生成される。
HeroMainを実行すると、trueが出力されるのが確認できる。

package ex;

public class Hero {
	String name;
	int hp;

	@Override
	public String toString() {
		return getClass().getName()
				+ "[name=" + name + ", hp=" + hp + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + hp;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Hero other = (Hero) obj;
		if (hp != other.hp)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}

HeroMainでh2の名前とHPを変更して、きちんと判定しているか確認する。

package ex;

public class HeroMain {

	public static void main(String[] args) {
		Hero h = new Hero();
		h.name = "ミナト";
		h.hp = 100;
		System.out.println(h.toString());
		Hero h2 = new Hero();
		h2.name = "ミナト";
		h2.hp = 100;
		System.out.println(h.equals(h2));
		h2.hp = 101;
		System.out.println(h.equals(h2));
		h2.hp = 100;
		System.out.println(h.equals(h2));
		h2.name = "ミナトA";
		System.out.println(h.equals(h2));
	}

}

実行結果は以下の通り。

ex.Hero[name=ミナト, hp=100]
true
false
true
false

コレクションフレームワーク
コレクションフレームワークは、複数のオブジェクトを扱う仕組みで、Javaではjava.utilパッケージにインタフェースやクラスが用意されている。

Listインタフェースの実装であるArrayListを使用した例。
add() : 要素を追加する。
remove() : 要素を削除する。
size() : 要素の数。
contains() : 要素が含まれているかどうかを調べる。

package ex;

import java.util.ArrayList;

public class ListMain {

	public static void main(String[] args) {
		ArrayList<String> list = new ArrayList<>();
		list.add("プレミアムモルツ");
		list.add("バドワイザー");
		list.add("ギネス");
		//System.out.println(list.get(1));
		list.add(0, "一番搾り");
		//System.out.println(list.get(1));
		// ループで要素を表示
		for (int i = 0; i < list.size(); i++) {
			System.out.println(i + ":" + list.get(i));
		}
		list.remove(0);
		for (String s : list) {
			System.out.println(s);
		}
		System.out.println(list.contains("一番搾り"));
		list.remove("ギネス");
		for (String s : list) {
			System.out.println(s);
		}
	}

}

Setインタフェースを実装したHashSetを使用した例。

package ex;

import java.util.HashSet;
import java.util.Set;

public class SetMain {

	public static void main(String[] args) {
		Set<String> set = new HashSet<>();
		set.add("マリオ");
		set.add("ルイージ");
		set.add("ピーチ");
		for (String s : set) {
			System.out.println(s);
		}
		set.add("マリオ");
		for (String s : set) {
			System.out.println(s);
		}
		System.out.println(set.contains("ピーチ"));
		set.remove("ピーチ");
		for (String s : set) {
			System.out.println(s);
		}
		System.out.println(set.contains("ピーチ"));
	}

}

実行結果は以下の通り。

マリオ
ピーチ
ルイージ
マリオ
ピーチ
ルイージ
true
マリオ
ルイージ
false

Mapインタフェースを実装したHashMapを使用した例。

package ex;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapMain {

	public static void main(String[] args) {
		Map<String, String> map = new HashMap<>();
		map.put("m", "モンスターハンター");
		map.put("d", "ドラゴンクエスト");
		map.put("p", "ポケットモンスター");
		System.out.println(map.get("m"));
		Set<String> set = map.keySet();
		for (String key : set) {
			String v = map.get(key);
			System.out.println(key + ":" + v);
		}
		System.out.println(map.containsKey("a"));
		map.put("p", "パズルアンドドラゴンズ");
		for (String key : set) {
			String v = map.get(key);
			System.out.println(key + ":" + v);
		}
	}

}

実行結果は以下の通り。

モンスターハンター
p:ポケットモンスター
d:ドラゴンクエスト
m:モンスターハンター
false
p:パズルアンドドラゴンズ
d:ドラゴンクエスト
m:モンスターハンター