プリミティブ型とラッパークラス

プリミティブ型とは

Javaにおけるデータ型は、大きく分けて プリミティブ型 と 参照型 の2つに分類されます。 プリミティブ型は、数値や文字などの「値そのもの」を直接保持するデータ型です。 Javaには以下の8つのプリミティブ型が用意されています。

分類 型名 保持するデータ
整数 byte, short, int, long 整数値
浮動小数点数 float, double 実数値
文字 char 1つの文字
真理値 boolean true または false

プリミティブ型はオブジェクトではないため、フィールドやメソッドを持ちません。 その代わり、メモリの消費量が少なく、動作が非常に高速であるという特徴を持っています。 プログラムは以下のようになります。

void setup() {
  int number = 10;
  double pi = 3.14;
  boolean flag = true;

  println(number);
  println(pi);
  println(flag);
}

実行結果

10
3.14
true

問題の発生:コレクションとの組み合わせ

プリミティブ型は効率的ですが、オブジェクトではないために生じる課題があります。 Javaのコレクションフレームワークやジェネリクスは、オブジェクトのみを扱うように設計されています。 そのため、ArrayList の型パラメータにプリミティブ型を指定することはできません。 プログラムは以下のようになります。

import java.util.ArrayList;

void setup() {
  // ArrayList<int> list = new ArrayList<int>(); // コンパイルエラー!
}

このプログラムは、ArrayList<int> という記述の部分でコンパイルエラーになります。 このように、オブジェクトとして数値を扱いたい場面において、プリミティブ型をそのまま利用できないという課題が発生します。

ラッパークラスによる解決

この課題を解決するために、各プリミティブ型に対応するオブジェクト型のクラスが用意されています。 これを ラッパークラス と呼びます。 ラッパークラスは、プリミティブ型の値をオブジェクトの中に包み込む役割を持ちます。

各プリミティブ型と対応するラッパークラスは以下の通りです。

プリミティブ型 対応するラッパークラス
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

例として Integerを用いて、数値をオブジェクトとして包み込み、ArrayList に追加するプログラムは以下のようになります。

import java.util.ArrayList;

void setup() {
  // ラッパークラスであるIntegerを指定してArrayListを生成する
  ArrayList<Integer> list = new ArrayList<Integer>();

  // int型の値をIntegerオブジェクトに変換して追加する
  list.add(Integer.valueOf(10));
  list.add(Integer.valueOf(20));

  // オブジェクトからint型の値を取り出す
  int value1 = list.get(0).intValue();
  int value2 = list.get(1).intValue();

  println(value1 + value2);
}

実行結果

30

Integer.valueOf(10) を用いることで、int 型の値を Integer オブジェクトに変換しています。 また、intValue() メソッドを使用することで、オブジェクトから再び int 型の値を取り出しています。 これにより、コレクションの中でも数値をオブジェクトとして安全に扱うことができます。

オートボクシングとアンボクシング

ラッパークラスを使うことでコレクションに数値を格納できるようになりました。 しかし、値を追加・取得するたびに Integer.valueOf() や intValue() を明示的に記述するのは非常に面倒です。 そこでJavaには、プリミティブ型とラッパークラスの型変換を自動で行う仕組みが用意されています。

1. オートボクシング

プリミティブ型の値が、対応するラッパークラスのオブジェクトへ自動的に変換される処理を オートボクシング と呼びます。 プログラムは以下のようになります。

void setup() {
  // 自動的に Integer.valueOf(100) に変換される
  Integer objectNumber = 100;

  println(objectNumber);
}

実行結果

100

2. アンボクシング

逆に、ラッパークラスのオブジェクトから、対応するプリミティブ型の値へ自動的に変換される処理を アンボクシング と呼びます。 プログラムは以下のようになります。

void setup() {
  Integer objectNumber = Integer.valueOf(200);

  // 自動的に objectNumber.intValue() が呼び出される
  int primitiveNumber = objectNumber;

  println(primitiveNumber);
}

実行結果

200

オートボクシングとアンボクシングのおかげで、ArrayList を用いる際にも、プリミティブ型とラッパークラスの違いをほとんど意識せずに記述できます。 プログラムは以下のようになります。

import java.util.ArrayList;

void setup() {
  ArrayList<Integer> list = new ArrayList<Integer>();

  // オートボクシングによって、int型をそのまま追加できる
  list.add(10);
  list.add(20);

  // アンボクシングによって、Integer型をint型として直接受け取れる
  int first = list.get(0);
  int second = list.get(1);

  println(first + second);
}

実行結果

30

オートボクシングは便利ですが、ラッパークラスのオブジェクトが null である場合にアンボクシングを行おうとすると、実行時に NullPointerException が発生します。 オブジェクトを扱う際は null の可能性に気を付けましょう。

ラッパークラスの便利なユーティリティ機能

ラッパークラスは、単に値を包み込むだけでなく、データ型の変換や情報の取得に役立つ多くの 静的メソッド や定数を提供しています。

1. 文字列から数値への変換

ユーザーから入力された文字列を数値として計算に使いたい場合、ラッパークラスのメソッドを用いて変換します。 プログラムは以下のようになります。

void setup() {
  String intString = "123";
  String doubleString = "45.67";

  // 文字列をそれぞれのプリミティブ型に変換する
  int intValue = Integer.parseInt(intString);
  double doubleValue = Double.parseDouble(doubleString);

  println(intValue + 10);
  println(doubleValue + 1.0);
}

実行結果

133
46.67

もし、数値として解釈できない文字列を変換しようとすると、 NumberFormatException というエラーが発生します。

2. 最大値・最小値の定数

各数値型のラッパークラスには、そのデータ型が表現できる最大値と最小値が定数として定義されています。 プログラムは以下のようになります。

void setup() {
  // int型とdouble型の最大値・最小値を出力する
  println("intの最小値: " + Integer.MIN_VALUE);
  println("intの最大値: " + Integer.MAX_VALUE);
  println("doubleの最小値: " + Double.MIN_VALUE);
  println("doubleの最大値: " + Double.MAX_VALUE);
}

実行結果

intの最小値: -2147483648
intの最大値: 2147483647
doubleの最小値: 4.9E-324
doubleの最大値: 1.7976931348623157E308

演習

演習1

プリミティブ型とラッパークラスのそれぞれの特徴を述べなさい。 また、ArrayList などのコレクションを使用する際に、プリミティブ型をそのまま使用できずラッパークラスを使用しなければならない理由を説明しなさい。

演習2

数字を表す文字列の配列を受け取り、それらを整数に変換して合計値を計算するプログラムを作成しなさい。 ただし、配列の中には数値として解釈できない不正な文字列が含まれている可能性があるため、その場合は変換エラーが発生したことを出力し、エラー以外の数値の合計値を出力しなさい。 以下の setup 関数が正しく動作するようにプログラムを設計しなさい。

void setup() {
  String[] inputs = {"10", "20", "abc", "30", "xyz"};
  int total = 0;

  for (String input : inputs) {
    try {
      // ここで文字列を整数に変換し、totalに加算する
    } catch (NumberFormatException e) {
      // エラー時の処理を記述する
      println("変換失敗: " + input);
    }
  }

  println("合計値: " + total);
}

実行結果

変換失敗: abc
変換失敗: xyz
合計値: 60

演習3

あらゆるオブジェクトを内部の配列に保持する、簡易的なリストクラス MyList を作成しなさい。 また、作成した MyList クラスにプリミティブ型である double や boolean を追加・取得したときに、オートボクシングおよびアンボクシングがどのように行われるかを確認しなさい。 以下の setup 関数が正しく動作するように MyList クラスを設計しなさい。

void setup() {
  MyList list = new MyList(5);

  // プリミティブ型の値をそのまま追加する
  list.add(3.14);
  list.add(true);

  // 値を取り出し、プリミティブ型の変数に代入する
  double value1 = (Double) list.get(0);
  boolean value2 = (Boolean) list.get(1);

  println("取得したdouble値: " + value1);
  println("取得したboolean値: " + value2);
}

実行結果

取得したdouble値: 3.14
取得したboolean値: true

演習の解答例

演習1の解答例

プリミティブ型の特徴: 値そのものをメモリに直接保持するため、メモリ消費が少なく動作が高速である。 しかし、オブジェクトではないためメソッドやフィールドを持たない。

ラッパークラスの特徴: プリミティブ型の値をオブジェクトとして包み込んだ参照型であり、各種変換メソッドなどの機能を持つ。 しかし、オブジェクトであるためメモリ消費やアクセス速度の面でプリミティブ型より劣る。

理由: ArrayList などのコレクションは、要素の格納や管理にオブジェクトへの参照を利用するように設計されているため。 プリミティブ型はオブジェクトではないため、参照が存在せず、コレクションの要素として直接格納することができない。

演習2の解答例

文字列を数値に変換する処理と、例外処理を組み合わせた合計計算プログラムの実装例は以下の通りである。 Integer.parseInt メソッドを用いて文字列を int 型に変換し、変換できない文字列が渡された場合に発生する NumberFormatException を try-catch 構文で捕捉してエラーを回避している。

sum.pde

void setup() {
  String[] inputs = {"10", "20", "abc", "30", "xyz"};
  int total = 0;

  for (String input : inputs) {
    try {
      // 文字列を整数に変換する
      int value = Integer.parseInt(input);
      total += value;
    } catch (NumberFormatException e) {
      println("変換失敗: " + input);
    }
  }

  println("合計値: " + total);
}

演習3の解答例

任意のオブジェクトを格納可能な MyList クラスと、オートボクシングを活用した検証プログラムの実装例は以下の通りである。 add メソッドの引数は Object 型であるため、プリミティブ値を渡すと自動的にラッパークラスに変換される。 取り出す際は、Object 型から一度ラッパークラスにキャストすることで、プリミティブ型変数への代入時に自動的に値が復元される。

MyList.java

class MyList {
  private Object[] elements;
  private int size = 0;

  public MyList(int capacity) {
    this.elements = new Object[capacity];
  }

  public void add(Object element) {
    this.elements[this.size] = element;
    this.size += 1;
  }

  public Object get(int index) {
    return this.elements[index];
  }
}

results matching ""

    No results matching ""