山崎屋の技術メモ

IT業界で働く中で、気になること、メモしておきたいことを書いていきます。

Java8 での日付妥当性チェック

Java8 の LocalDate と DateTimeFormatter を使用した日付妥当性チェックについてです。

2014 年に公開された Java8 を使用した日付妥当性チェックですが、すでに多くのサイトでサンプルが掲載されています。ただ、少し気になる実装も紹介されており、自分なりの正しい日付チェック方法を残しておきます。

	public static boolean isCorrectDate(String dateStr) {
		if (dateStr == null || dateStr.length() != 10) {
			return false;
		}
		DateTimeFormatter df = DateTimeFormatter.ofPattern("uuuu/MM/dd").withResolverStyle(ResolverStyle.STRICT);
		try {
			LocalDate.parse(dateStr, df);
		} catch (DateTimeParseException e) {
			return false;
		}
		return true;
	}

最初に引数の null チェックと 10 文字であるかどうかのチェックを行います。10 文字であるかどうかのチェックを行わない場合、「-99999/01/01」が妥当な日付と判断されてしまいます(「99999/01/01」は期待通りに、正しくない日付と判断します。不思議。。。)。マイナスの年を妥当な日付と判断することが要件に添っている状況は稀だと思うので、マイナスは問答無用で正しくない日付と判断してしまいます。

使用例を以下に示します。

	public static void main(String... args) {
		System.out.println(isCorrectDate("0000/01/01"));  // true
		System.out.println(isCorrectDate("9999/12/31"));  // true
		System.out.println(isCorrectDate("2017/02/28"));  // true
		System.out.println(isCorrectDate("2017/02/29"));  // false  うるう年でないので 2/29 は存在しない。
		System.out.println(isCorrectDate("2020/02/28"));  // true
		System.out.println(isCorrectDate("2020/02/29"));  // true 2020 年はうるう年。東京オリンピックの年。
		System.out.println(isCorrectDate("02020/02/29")); // false 年が 5 桁。
		System.out.println(isCorrectDate("2020/2/29"));   // false 月が 1 桁。
		System.out.println(isCorrectDate("2020/02/1"));   // false 日が 1 桁。
		System.out.println(isCorrectDate("202a/02/01"));  // false 年にアルファベット混入。
		System.out.println(isCorrectDate("2020/0a/01"));  // false 月にアルファベット混入。
		System.out.println(isCorrectDate("2020/01/0a"));  // false 日にアルファベット混入。
	}


でわ。

Java8ではじめる「ラムダ式」 (I・O BOOKS)

Java8ではじめる「ラムダ式」 (I・O BOOKS)

Javaによる関数型プログラミング ―Java 8ラムダ式とStream

Javaによる関数型プログラミング ―Java 8ラムダ式とStream

わかりすぎるJava8の教科書 (SCC Books 376)

わかりすぎるJava8の教科書 (SCC Books 376)

現場で使える[最新]Java SE 7/8 速攻入門

現場で使える[最新]Java SE 7/8 速攻入門

【Spring Framework】bean名による@Autowired

前回の記事で Spring Framework による簡単な DI を説明した。

yyama1556.hateblo.jp

これはプロパティの型を手掛かりに Spring が DI してくれていて、"byType" によるインジェクションという。

では、プロパティの型と同じクラスが2つ以上存在した場合はどちらをDIしてくれるのだろうか。

今回は bean の名前によるインジェクションである "byName" によるインジェクションを紹介する。

"byType" なのにプロパティの型と同じクラスが2つ以上存在した場合の挙動

フォルダ構成はこう。
f:id:yyama1556:20160810085417p:plain

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
      http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context
      http://www.springframework.org/schema/context/spring-context.xsd">
	<context:component-scan base-package="org.yyama.bean" />
</beans>

[org.yyama.bean]パッケージ配下のクラスをSpringコンテキストの管理対象としている。

Mainクラス

package org.yyama;

import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.yyama.bean.Fuga;

public class Main {
	public static void main(String... args) {
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
		Fuga fuga = ctx.getBean(Fuga.class);
		fuga.proc();
		ctx.close();
	}
}

Spring コンテナから Fuga のインスタンス fuga を取得し、fuga.proc() メソッドを実行している。

Fuga クラス

package org.yyama.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Fuga {
	@Autowired()
	HogeInterface hoge;

	public void proc() {
		hoge.print();
	}
}

ここの hoge プロパティには HogeInterface をインプリメントした実装クラスがインジェクションされるのだが、今回はHogeInterface をインプリメントしたクラスが2つ存在する。その場合どのような挙動になるのかを実験する。

HogeInterfaceインターフェース

package org.yyama.bean;

public interface HogeInterface {
	public void print();
}

HogeImplAクラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component
public class HogeImplA implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplAのproc()");
	}
}

print() メソッドで "HogeImplAのproc()" と出力している。

HogeImplBクラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component
public class HogeImplB implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplBのproc()");
	}
}

print() メソッドで "HogeImplBのproc()" と出力している。

Mainクラスを実行すると・・・

エラーが出力された。簡単に訳すと「fugaの生成に失敗した。hogeフィールドの依存性が満たされない。[org.yyama.bean.HogeInterface]が定義されているが、これにひとつだけ一致することを期待していたが、2つ見つかった。hogeImplAとhogeImplBだ。」のように出力されている。

8 10, 2016 8:55:40 午前 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 08:55:40 JST 2016]; root of context hierarchy
8 10, 2016 8:55:40 午前 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [applicationContext.xml]
8 10, 2016 8:55:41 午前 org.springframework.context.support.ClassPathXmlApplicationContext refresh
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fuga': Unsatisfied dependency expressed through field 'hoge': No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'fuga': Unsatisfied dependency expressed through field 'hoge': No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:569)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:349)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:776)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:541)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
	at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
	at org.yyama.Main.main(Main.java:8)
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [org.yyama.bean.HogeInterface] is defined: expected single matching bean but found 2: hogeImplA,hogeImplB
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:172)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1065)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1019)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:566)
	... 15 more

byName によるインジェクションに修正する。

Fuga クラス、HogeImplA クラス、HogeImplB クラスを修正する。

Fuga クラス

package org.yyama.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

@Component
public class Fuga {
	@Autowired()
	@Qualifier("hogeA")
	HogeInterface hoge;

	public void proc() {
		hoge.print();
	}
}

"@Autowired()" の下に、"@Qualifier("hogeA")" を追加した。これで [hogeA] という名前の bean をインジェクションするようSpring に要求している。

HogeImplAクラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component("hogeA")
public class HogeImplA implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplAのproc()");
	}
}

"@Component" アノテーションに引数を追加している。これでこのクラスのインスタンスは "hogeA" という名前で Spring に管理される。

HogeImplB クラス

package org.yyama.bean;

import org.springframework.stereotype.Component;

@Component("hogeB")
public class HogeImplB implements HogeInterface {
	@Override
	public void print() {
		System.out.println("HogeImplBのproc()");
	}
}

もうお分かりだろうが、"@Component" アノテーションに引数を追加している。これでこのクラスのインスタンスは "hogeB" という名前で Spring に管理される。

これで "HogeInterface" をインプリメントした実装クラスは Spring コンテナ内に2つ存在するが、名前が異なるので見分けがつくということだ。

実行結果

8 10, 2016 9:11:18 午前 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
情報: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 09:11:18 JST 2016]; root of context hierarchy
8 10, 2016 9:11:18 午前 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [applicationContext.xml]
HogeImplAのproc()
8 10, 2016 9:11:18 午前 org.springframework.context.support.ClassPathXmlApplicationContext doClose
情報: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4d405ef7: startup date [Wed Aug 10 09:11:18 JST 2016]; root of context hierarchy

Fuga クラスで "@Qualifier("hogeA")" と指定しているので、Fuga クラスの hoge プロパティには "hogeA" という名前の beanがインジェクションされたことがわかる。

今日はここまで。


Spring 関連記事へのリンク集つくりました。


【Spring MVC】Model に登録されているオブジェクトの一覧を表示する

前回の記事で、サーバ側(Controller)で設定した値を JSP で表示するサンプルを掲載した。その際に Spring で用意されている、 Model オブジェクトに JSP で使用したいオブジェクトをセットした。

yyama1556.hateblo.jp

今回はこの Model に登録されているオブジェクトの一覧を表示する方法、および、識別名をキーにオブジェクトの存在有無を確認するサンプルをメモしておく。

オブジェクトの一覧を表示する

難しいことは何もないので、まずソース(抜粋)。

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Model model) {
		// (1) String
		model.addAttribute("att1", "ほげ");

		// (2) List
		List<String> list = new ArrayList<>();
		list.add("リスト1");
		list.add("リスト2");
		list.add("リスト3");
		model.addAttribute("att2", list);

		// (3) 独自クラス
		ClassA classA = new ClassA();
		classA.setName("山崎屋");
		classA.setAge(19);
		model.addAttribute("att3", classA);

		// (4) model に登録されているオブジェクトをすべて表示
		model.asMap().entrySet().stream().forEach(s -> System.out.println(s));

		return "home";
	}

(1)~(3) でそれぞれ String/List/独自クラス のオブジェクトを生成し、model に addAttribute している。

そして (4) で model に登録されているオブジェクトの一覧を標準出力に出力している。Stream を使用しているので、見慣れていない人は何をしているのか分かりにくいかも知れないが、ポイントは「model.asMap()」の部分。ここで、model に登録されているオブジェクトをマップにして返している。

Map のキーが識別名(att1、att2、att3)の文字列で、Map の値が登録したオブジェクトである。標準出力にはこのオブジェクトの toString() の戻り値が表示される。

このメソッドを実行した際の標準出力はこちら。

att1=ほげ
att2=[リスト1, リスト2, リスト3]
att3=org.yyama.web.HomeController$ClassA@20a4e442

ちなみに、Stream を使用しない場合、 (4)は次のようになる。

		// (4) model に登録されているオブジェクトをすべて表示
		for (Map.Entry<String, Object> e : model.asMap().entrySet()) {
			System.out.println(e);
		}

model への登録有無を確認する。

model オブジェクトには containsAttribute というインスタンスメソッドが用意されており、第一引数に識別名を文字列で渡してあげることにより、その識別名が登録されているか否か、bool 値を返してくれる。

サンプルソースはこちら。

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Model model) {
		// (1) String
		model.addAttribute("att1", "ほげ");

		// (2) List
		List<String> list = new ArrayList<>();
		list.add("リスト1");
		list.add("リスト2");
		list.add("リスト3");
		model.addAttribute("att2", list);

		// (3) 独自クラス
		ClassA classA = new ClassA();
		classA.setName("山崎屋");
		classA.setAge(19);
		model.addAttribute("att3", classA);

		// (5) model への登録有無を確認
		System.out.println(model.containsAttribute("att1")); // true
		System.out.println(model.containsAttribute("att2")); // true
		System.out.println(model.containsAttribute("att3")); // true
		System.out.println(model.containsAttribute("att4")); // false

		return "home";
	}

コメントで記載している値が標準出力に出力される。

↓↓↓ Spring Framework 関連の記事をまとめました。
yyama1556.hateblo.jp

今日はここまで。

わかりやすいJava EE ウェブシステム入門

わかりやすいJava EE ウェブシステム入門

【Spring MVC】サーバー側(Controller)で設定した値を JSP で使用する

Web アプリケーションでの基本的な動きだが、サーバー側で作成したオブジェクトを JSP で表示(利用)することは必ずある。今回は Spring MVC を使用したサンプルを紹介する。

実行結果イメージ

まず、サンプルの実行結果を掲載しておく。
f:id:yyama1556:20170430133140p:plain

Controller 側

コントローラのサンプル(抜粋)がこちら。

@Controller
public class HomeController {

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Model model) {
		// String
		model.addAttribute("att1", "ほげ");

		// List
		List<String> list = new ArrayList<>();
		list.add("リスト1");
		list.add("リスト2");
		list.add("リスト3");
		model.addAttribute("att2", list);

		// 独自クラス
		ClassA classA = new ClassA();
		classA.setName("山崎屋");
		classA.setAge(19);
		model.addAttribute("classA", classA);

		return "home";
	}

コントローラクラスなので、クラス宣言に @Controller を付与している。home メソッドには @RequestMapping アノテーションを付与し、クライアントからのリクエストを処理することを宣言している。

@RequestMapping については以下の記事で紹介している。
【Spring MVC】@RequestMapping の基本 - 山崎屋の技術メモ

さて本題。
home メソッドでは Model オブジェクトを引数で受け取っている。これは Spring が用意したクラスで、JSP に渡したいオブジェクトを addAttribute することになる。

サンプルでは String、リスト、独自クラスの3つを addAttribute している。

独自クラスのフィールドには、対応する Getter が必要なので注意する(JSP が Getterを使用してフィールドの値を取得している)。独自クラスのソースは後で紹介する。

addAttribute の第一引数は識別子。JSP でオブジェクトを取り出すときに使う名前。値はなんでも良いが、分かりやすい名前を付ける。サンプルでは att1、att2、att3 としている。

第二引数は JSP に渡したいオブジェクトを指定する。

JSP

こちらもまずソースコード(抜粋)。

	<h3>String</h3>
	${att1}<br />
	<br />
	
	<h3>List 要素指定</h3>
	${att2[0]}<br />
	${att2[1]}<br />
	${att2[2]}<br />
	<br />
	
	<h3>List ForEachでまわす</h3>
	<c:forEach var="str" items="${att2}">
		<c:out value="${str}" /><br />
	</c:forEach>
	<br />
	
	<h3>独自クラス</h3>
	${att3.name}<br />
	${att3.age}<br />
	<br />

Model に addAttribute したときに第一引数に指定した識別子を ${} で囲むことで、オブジェクトの値を取得できる。リストの要素を指定して取得するときは ${att2[0]} のように [] の中に要素を数字で指定する。forEach でまわすときは JSTL の c:forEach を使用する。

独自クラスのフィールドにアクセスしたい場合、 ${} 内で「識別子.フィールド名」のように指定する。

ソース全量

最後にソースの全量を掲載しておく。

Controller。

package org.yyama.web;

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

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

@Controller
public class HomeController {

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Model model) {
		// String
		model.addAttribute("att1", "ほげ");

		// List
		List<String> list = new ArrayList<>();
		list.add("リスト1");
		list.add("リスト2");
		list.add("リスト3");
		model.addAttribute("att2", list);

		// 独自クラス
		ClassA classA = new ClassA();
		classA.setName("山崎屋");
		classA.setAge(19);
		model.addAttribute("att3", classA);

		return "home";
	}

	// 独自クラスの宣言
	public class ClassA {
		private String name;
		private int age;

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

		public String getName() {
			return name;
		}

		public void setAge(int age) {
			this.age = age;
		}

		public int getAge() {
			return age;
		}

	}
}

JSP

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page language="java" contentType="text/html; charset=utf-8"
	pageEncoding="utf-8"%>
<html>
<head>
<title>Home</title>
</head>
<body>
	<h3>String</h3>
	${att1}<br />
	<br />
	
	<h3>List 要素指定</h3>
	${att2[0]}<br />
	${att2[1]}<br />
	${att2[2]}<br />
	<br />
	
	<h3>List ForEachでまわす</h3>
	<c:forEach var="str" items="${att2}">
		<c:out value="${str}" /><br />
	</c:forEach>
	<br />
	
	<h3>独自クラス</h3>
	${att3.name}<br />
	${att3.age}<br />
	<br />
	
</body>
</html>

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/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.yyama</groupId>
	<artifactId>web</artifactId>
	<name>Sample</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<org.springframework-version>4.3.8.RELEASE</org.springframework-version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>
	<dependencies>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

	</dependencies>
</project>

以上。

↓↓↓ Spring Framework 関連の記事をまとめました。
yyama1556.hateblo.jp

SpringによるWebアプリケーションスーパーサンプル 第2版

SpringによるWebアプリケーションスーパーサンプル 第2版

java の例外設計

プロジェクトの設計フェーズ序盤で、例外の扱いについて方針を決める必要がある。

もし、自分が方針を決めるとしたらこうするというものをメモしておく。

1.検査例外と非検査例外の使い分け

最近では検査例外を悪と考え、すべて非検査例外にラップしてスローしなおすという場合もあるようだ。

これを機能として提供しているフレームワークもある。

しかし、検査例外と非検査例外は理由があって分かれているので、私はちゃんと使い分けるべきと考えている。

検査例外

プログラムが正しくても起こりうる例外。

例えば、

  • 読み込もうとしたファイルが他のプログラムにより消された
  • デッドロックした
  • ユーザがブラウザの戻るボタンを使って、想定外の操作をした

などである。こういう場合には検査例外を使用する。

「オイラの処理には防ぎきれないエラーが発生する可能性があるので、注意しておいてね」というのがtry ~ catch の強制(または throws 区の強制)である。

なので特別な理由がなければ非検査例外にラップしてスローなどは行わない。

非検査例外

プログラム内で発生を防げるにもかかわらず発生した例外。

Null チェックを怠ったために発生した NullPointerException 。配列のインデックスをチェックしていなかったために発生した
ArrayIndexOutOfBoundsException 等。

これらの例外が発生したのであれば、プログラムのバグとして、プログラムを修正する。

2.例外のスローの仕方

できるだけ、細かくスローする。

FileNotFoundException と IOException の両方が発生する可能性がある場合、メソッド宣言の throws 区には IOException だけを書くこともできる。(IOException は FileNotFoundException の親クラス。)

だけど、特別な理由がない限りは両方書くべき。

例外を受け取ったクラスが、その例外をどのように扱うかは呼び出し先のクラスは関知できない。

だから、例外のハンドリングに必要と思われる情報は、なるべく細かく伝えてあげる必要がある。

3.例外のキャッチの仕方

細かくキャッチするか、親クラスでまとめてキャッチするか、極論を言えば「catch (Exception e)」としてしまうか、全体の方針として明確にしない。

責任を持ってハンドリングするという目的が達成できるのであれば、どのようにキャッチするかは各処理の設計にゆだねる。

4.独自例外

なるべく作らない。Java 標準の例外で目的に沿うものがあればそれを使う。

業務独自の例外を作らなければ対応できない場合のみ作る。

無条件に独自例外にラップしてスローしているシステムも見かけたことがあるが、まるで意味がなかった。そのプロジェクトで出力される例外のスタックトレースには、まず独自例外の出力があり、その後、Java 標準の例外出力がある。そして結局 Java 標準の例外メッセージを見て解決策を考えることになる。

まとめ

今のところこんな感じで整理している。追加や変更は随時行っていく。

もちろんプロジェクトの特性により、当記事の方針では実装できない(しにくい)場合も必ずあるので、絶対に守らないとダメっていうものでもない。

でわ。

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)