はじめに
自己紹介
皆さん、こんにちは、Udemy講師の斉藤賢哉です。私はこれまで、25年以上に渡って企業システムの開発に携わってきました。特にアーキテクトとして、ミッションクリティカルなシステムの技術設計や、Javaフレームワーク開発などの豊富な経験を有しています。
様々なセミナーでの登壇や雑誌への技術記事寄稿の実績があり、また以下のような書籍も執筆しています。
いずれもJava EE(Jakarta EE)を中心にした企業システム開発のための書籍です。中でも 「アプリケーションアーキテクチャ設計パターン」は、(Javaに限定されない)比較的普遍的なテーマを扱っており、内容的にはまだまだ陳腐化していないため、興味のある方は是非手に取っていただけると幸いです(中級者向け)。
Udemy講座のご紹介
この記事の内容は、私が講師を務めるUdemy講座『Java Basic編』の一部の範囲をカバーしたものです。『Java Basic編』はこちらのリンクから購入できます(セールス対象外のためいつも同じ価格)。また定価の約30%OFFで購入可能なクーポンをQiita内で定期的に発行していますので、興味のある方は、ぜひ私の他の記事をチェックしてみてください。
この講座は、以下のような皆様にお薦めします。
- Javaの言語仕様や文法を正しく理解すると同時に、現場での実践的なスキル習得を目指している方
- 新卒でIT企業に入社、またはIT部門に配属になった、新米システムエンジニアの方
- 長年IT部門で活躍されてきた中堅層の方で、学び直し(リスキル)に挑戦しようとしている方
- 今後、フリーランスエンジニアとしてのキャリアを検討している方
- 「Chat GPT」のエンジニアリングへの活用に興味のある方
- 「Oracle認定Javaプログラマ」の資格取得を目指している方
- IT企業やIT部門の教育研修部門において、新人研修やリスキルのためのオンライン教材をお探しの方
この記事を含むシリーズ全体像
この記事はJava SEの一部の機能・仕様を取り上げたものですが、一連のシリーズになっており、シリーズ全体でJava SEを網羅しています。また認定資格である「Oracle認定Javaプログラマ」(Silver、Gold)の範囲もカバーしています。シリーズの全体像および「Oracle認定Javaプログラマ」の範囲との対応関係については、以下を参照ください。
17.1 ラッパークラスの特徴とAPI
チャプターの概要
このチャプターでは、プリミティブ型をオブジェクトとして扱うためのラッパークラスの特徴やAPIについて学びます。
17.1.1 ラッパークラスの概要
ラッパークラスとは
Javaのデータ型には、int型やdouble型といったプリミティブ型が言語仕様として規定されており、データを格納したり、格納したデータに対して演算や判定といった処理を行うことが可能です。ただしこれらの型はオブジェクトとして扱うことはできないため、以下のような制約がありました。
• 自身の値を操作するためのAPIを持つことができない。
• 初期値が何らかの値になり(例えばint型の場合は0)、null値を持つことができない。
• 総称型の型パラメータに指定することができない。※総称型についてはチャプター18.1および『Java Advanced編』参照
そこでJavaでは、プリミティブ型をオブジェクトとして扱うために、ラッパークラスと呼ばれるクラス群がJava SEのクラスライブラリとして提供されています。ラッパークラスとはその名の通り、プリミティブ型をラップして、機能を追加するためのクラスです。
ラッパークラスには、プリミティブ型に対応する形で以下のようなラインアップがあります。
【表17-1-1】ラッパークラスの全体像
分類 | プリミティブ型 | ラッパークラス | |
---|---|---|---|
数値型 | 整数型 | byte | java.lang.Byte |
short | java.lang.Short | ||
int | java.lang.Integer | ||
long | java.lang.Long | ||
浮動小数点型 | float | java.lang.Float | |
double | java.lang.Double | ||
論値型 | boolean | java.lang.Boolean | |
文字型 | char | java.lang.Character |
なおラッパークラスはStringクラスなどと同様にイミュータブルのため、フィールドの型として宣言しても「マルチスレッド環境における不正な更新」(『Java Advanced編』参照)は発生しません。
ラッパークラス⇔プリミティブ型の相互変換
ラッパークラスとプリミティブ型は、暗黙的に相互変換することが可能です。以下のコードを見てください。
int x = 30;
Integer val = x; //【1】オートボクシング
int y = val; //【2】オートアンボクシング
このようにint型の値は、ラッパークラスInteger型変数にそのまま代入することができます【1】。この機能をオートボクシングと呼びます。ラッパークラスにはコンストラクタは定義されていますが非推奨になっており、初期化はこのコードのようにオートボクシングによって行います。
またInteger型のオブジェクトは、int型変数にそのまま代入することができます【2】。この機能をオートアンボクシングと呼びます。
ラッパークラスとプリミティブ型の使い分け
前項で説明したように、オートボクシング・アンボクシングにより、ラッパークラスとプリミティブ型はほとんど意識することなく相互に変換可能です。
それでは、そもそもラッパークラスとプリミティブ型は、どのように使い分けるべきでしょうか。
まず開発者が「モノ」や「概念」をクラスとして作成する場合、その属性(フィールド)として数値型の値を持つのであれば、ラッパークラスを使った方が良いでしょう。
その理由を説明するために、「人物」を表すPersonクラスがあり、属性として名前、年齢を持つ、というケースを考えます。PersonクラスにはPerson(String)
といったコンストラクタがあり、名前のみで初期化が可能(年齢は初期化不要)だとします。このとき、Personクラスの年齢を表すフィールドは整数のため、ラッパークラスInteger型か、プリミティブint型が候補になりますが、仮にint型を選択すると、年齢が初期化されなかった場合に初期値である0を保持することになります。これでは年齢が初期化されなかったのか、0歳という年齢を表すのかを、識別することはできません。その点Integer型であれば、null値を持つことができるため、初期化されたかどうかを識別することができます。
では常にラッパークラスの方が万能かというと、必ずしもそんなことはありません。ラッパークラスはあくまでもオブジェクトであり、オブジェクトの生成やオートボクシング・アンボクシングには相応のコストがかかります。従って、特に初期化を意識する必要がないローカル変数としての利用や、数値型の値に対して繰り返し計算を行うような処理では、低コストなプリミティブ型の方が適していると言えます。
以上から、ラッパークラスとプリミティブ型は、以下のような方針で使い分けると良いでしょう。
- 主にクラスのフィールドとして使用し、初期化未済であることを識別したい場合は、ラッパークラス
- ローカル変数として使用したり、繰り返し何らかの計算を行ったりする場合は、プリミティブ型
17.1.2 ラッパークラスのAPI
NumberクラスのAPI
ラッパークラスの中でも数値を表す各クラス、具体的にはByteクラス、Shortクラス、Integerクラス、Longクラス、Floatクラス、Doubleクラスは、いずれもjava.lang.Numberクラスという抽象クラスの子クラスです。
Numberクラスには、以下のようなAPIが定義されています。
API(メソッド) | 説明 |
---|---|
byte byteValue() | このNumberをプリミティブbyte型として返す。 |
short shortValue() | このNumberをプリミティブint型として返す。 |
int intValue() | このNumberをプリミティブint型として返す。 |
long longValue() | このNumberをプリミティブlong型として返す。 |
float floatValue() | このNumberをプリミティブfloat型として返す。 |
double doubleValue() | このNumberをプリミティブdouble型として返す。 |
いずれのAPIも、自身のオブジェクトをプリミティブ型の各データに変換するためのものですが、オートボクシングによってそのままラッパークラス型で受け取ることも可能です。後述するNumberFormatクラスのparse()メソッドなど、Java SEのAPIによってはNumber型を返すものがありますが、Number型のままでは取り扱いに制限があるため、これらのAPIを呼び出して適切な型に変換すると良いでしょう。
なおNumberクラスの子クラスには他にも種類がありますが、チャプター17.2で取り上げるjava.math.BigDecimalクラスも子クラスの1つです。
【図17-1-1】クラス図(Numberクラスとその子クラス)
整数ラッパークラスの定数とAPI
前述したように整数を表すラッパークラスには、Byteクラス、Shortクラス、Integerクラス、Longクラスの4種類があります。いずれのクラスも定義されている定数やAPIのラインアップはほとんど同じのため、ここではこれらのクラスの中でも、特に利用頻度が高いIntegerクラスを代表として取り上げます。
まずIntegerクラスには、以下のような定数が定義されています。
• MAX_VALUE … Integer型の正の最大値
• MIN_VALUE … Integer型の正の最小値
またIntegerクラスには、以下のようなAPIが定義されています。
API(メソッド) | 説明 |
---|---|
String toString() | この数値を文字列化して返す。 |
boolean equals(Object) | この数値と指定されたオブジェクトの等価性を判定して返す。指定されたオブジェクトがInteger型以外の場合は、falseを返す。 |
int compareTo(Integer) | このIntegerと指定されたIntegerの大小関係を比較する。比較の結果、自身の方が小さい場合は負の値を、大きい場合は正の値を、等しい場合は0を、それぞれ返す。 |
static String toString(int) | 指定された数値を、文字列化して返す。 |
static String toBinaryString(int) | 指定された数値の2進数表現を、文字列で返す。 |
static String toOctalString(int) | 指定された数値の8進数表現を、文字列で返す。 |
static String toHexString(int) | 指定された数値の16進数表現を、文字列で返す。 |
static int parseInt(String) | 指定された文字列を解析し、プリミティブint型で返す。文字列の形式がint型として解析できない場合は、NumberFormatException例外を送出する。 |
static int parseInt(String, int) | 指定された文字列を解析し、指定された値を基数としたプリミティブint型で返す。例えば、parseInt("-FF", 16)の場合、整数"-255"が返される。文字列の形式がint型として解析できない場合は、NumberFormatException例外を送出する。 |
static Integer valueOf(String) | 指定された文字列を解析し、Integer型で返す。文字列の形式がint型として解析できない場合は、NumberFormatException例外を送出する。 |
整数ラッパークラスのAPI使用方法
IntegerクラスのAPIの中で比較的よく使われるものに、parseInt()メソッドがあります。このメソッドはスタティックなメソッドで、コマンドライン変数など、外部から渡された文字列を数値に変換する場合などに使用します。
具体的には以下のコードのとおりです。
String str = "100"; // 文字列"100"
int val = Integer.parseInt(str); // 数値100
またこれらのAPIの中で特に注意が必要なのが、equals()メソッドです。ラッパークラスは、ボクシング・アンボクシングによって暗黙的にプリミティブ型と相互変換されますが、あくまでもオブジェクトである、ということを忘れてはなりません。従ってStringクラスと同じように、ラッパークラスで等価性(同じ数値であること)を判定するためには、==
演算子ではなく、以下のようにequals()メソッドを使用するようにしてください。
Integer val1 = 10;
Integer val2 = 10;
boolean flag = val1.equals(val2); // true
同じような理由から、ラッパークラス同士の大小関係を比較する場合も、<
や>
といった比較演算子ではなく、compareTo()メソッドを使用します。
浮動小数点ラッパークラスの定数とAPI
ここでは、浮動小数点数を表すラッパークラスである、FloatクラスとDoubleクラスの定数やAPIについて説明します。ただしそれらのラインアップは両クラスでほとんど同じなので、代表してDoubleクラスを取り上げます。
まずDoubleには、以下のような定数が定義されています。
- NaN … double型の非数(Not A Number)
- MAX_VALUE … double型の正の最大値
- MIN_VALUE … double型の正の最小値
- POSITIVE_INFINITY … double型の正の無限大(Infinity)
- NEGATIVE_INFINITY … double型の負の無限大(-Infinity)
この中でNaNとは「非数」という意味で、例えば算術演算において「負の値の平方根」を求めようとすると、この値が返ります。またPOSITIVE_INFINITYやNEGATIVE_INFINITYは「無限大」を表し、例えば浮動小数点の算術処理で「ゼロ除算」を行うと、この値が返ります。
次にDoubleクラスのAPIですが、基本的にはIntegerクラスとほどんど同様です。
Doubleクラス固有のAPIには、以下のようなものがあります。
API(メソッド) | 説明 |
---|---|
boolean isNaN() | この数値がNaNの時、trueを返す。 |
static boolean isNaN(double) | 指定された数値がNaNの時、trueを返す。 |
boolean isInfinite() | この数値が無限大の時、trueを返す。 |
static boolean isInfinite(double) | 指定された数値が無限大の時、trueを返す。 |
このチャプターで学んだこと
このチャプターでは、以下のことを学びました。
- ラッパークラスの概念や種類について。
- ラッパークラス⇔プリミティブ型の相互変換(オートボクシング・オートアンボクシング)について。
- ラッパークラスの主要なAPIについて。