連載
» 2010年09月09日 00時00分 UPDATE

【改訂版】Eclipseではじめるプログラミング(18):強く型付けされているJavaの理解に必修の“型変換” (1/3)

これからプログラミングを学習したい方、Javaは難しそうでとっつきづらいという方のためのJavaプログラミング超入門連載です。最新のEclipse 3.4とJava 6を使い大幅に情報量を増やした、連載「Eclipseではじめるプログラミング」の改訂版となります

[小山博史,株式会社ガリレオ]

プログラマは特に知っておきたい型変換の常識

 今回は、さまざまな「型変換」について説明します。また、型変換に関連する基本データ型の「ラッパークラス」や文法事項についても解説します。型変換には「暗黙の変換」「明示的な変換」があるので、注意が必要です。

 特に暗黙の変換について理解していないと、誤って自動変換された値を使ってしまい、プログラムの結果が思ったとおりにならず大変なことになってしまうことがあるからです。

 またJavaには、「オーバーロード」という便利な機能もあるので、これについても説明します。型変換の説明をするに当たり、オーバーロードの機能を使用したクラスメソッドが出てくる都合上、まずは、オーバーロードから説明します。

 EclipseでJavaプログラミングを始める準備がまだの方は、連載第1回の「Eclipse 3.4で超簡単Javaプログラミング基礎入門」で準備をしておいてください。

パラメータ型が別でも同じメソッド名?「オーバーロード」

 オブジェクト指向プログラミングでは、プログラムを作成するに当たって、「どんなオブジェクトが関係するのか」を分析しながらクラス設計します。このとき、クラスは「ほかのクラスのオブジェクトへ、どんな機能を提供するか」を決めることにより、「内部的にどんな情報を保持するか」「どんな操作を持つか」といったことを決めます。

 もちろん、「どんな情報をひとまとまりにして扱いたいのか」を決めてから、それらを扱うのに必要な操作を決めるという場合もあります。いずれにせよ、オブジェクトを中心に設計していきます。

 これまで学んできたとおり、Javaのクラスでは、「どんな情報を持つのか」はフィールドで宣言しますし、「どんな操作を持つのか」はメソッドで宣言します。あるクラスの提供する機能を、ほかのオブジェクトが利用するには、そのクラスのメソッドを呼び出します。このとき、機能としては同じようなものを提供するメソッドを複数持ちたいことがあります。

コード例で考えてみよう

 具体例を使って考えてみましょう。「int型やdouble型、boolean型、java.lang.Object型のデータについて、値を表示する」クラスを作成するとしましょう。ただし、Object型はtoStringメソッドで取得できる値を使います。それぞれのデータ型に応じてメソッドを用意して、次のように実装できます。

package sample18;
 
public class Sample01 {
    static class PrintApp {
        public void printInt(int v) {
            Integer i = Integer.valueOf(v);
            String s = i.toString();
            System.out.println(s);
        }
        public void printDouble(double v) {
            Double d = Double.valueOf(v);
            String s = d.toString();
            System.out.println(s);
        }
        public void printBoolean(boolean v) {
            Boolean b = Boolean.valueOf(v);
            String s = b.toString();
            System.out.println(s);
        }
        public void printObject(Object v) {
            String s = v.toString();
            System.out.println(s);
        }
    }
    public static void main(String[] args) {
        PrintApp app = new PrintApp();
        app.printInt(1);
        app.printDouble(2.0);
        app.printBoolean(false);
        app.printObject(new Object());
    }
}
sample18/Sample01.java

 それぞれのメソッドは受け取った値を「ラッパークラス」といわれるクラスを使って文字列の値に変換してから、System.out.println()で出力しています。ラッパークラスについては後ほど説明するので、ここでは「このようにすると、文字列に変換できる」ことだけ理解しておいてください。

 出来上がったクラスを実行すると、次のようになります。Object型は実行するたびに値が変わります。「java.lang.Object@」で始まる文字列です。

1
2.0
false
java.lang.Object@1a758cb
Sample01.javaの実行結果

 このように実装できますが、気になるところがあったと思います。そうです。ここでは、メソッドをたくさん作成しましたが、どのメソッドも「データを表示する」という機能であることに変わりはありません。同じ機能ですから、これらのメソッドについてメソッド名をprintとすることはできないのでしょうか。

オーバーロードの機能を使うと

 実は、Javaではパラメータの型が別であれば、同名のメソッドを宣言できます。この機能を「オーバーロード(overloading)」といいます。

 オーバーロードの機能を使うと、先ほどの例は下記のように記述できます。

package sample18;
 
public class Sample02 {
    static class PrintApp {
        public void print(int v) {
            Integer i = Integer.valueOf(v);
            String s = i.toString();
            System.out.println(s);
        }
        public void print(double v) {
            Double d = Double.valueOf(v);
            String s = d.toString();
            System.out.println(s);
        }
        public void print(boolean v) {
            Boolean b = Boolean.valueOf(v);
            String s = b.toString();
            System.out.println(s);
        }
        public void print(Object v) {
            String s = v.toString();
            System.out.println(s);
        }
    }
    public static void main(String[] args) {
        PrintApp app = new PrintApp();
        app.print(1);
        app.print(2.0);
        app.print(false);
        app.print(new Object());
    }
}
sample18/Sample02.java

 どのメソッドもメソッド名をprintとして、パラメータのデータ型を変更しています。同じ名前のメソッドをこんなに用意していいのか心配になるかもしれませんが、コンパイルエラーも起きませんし、実行すると、Sample01と同じ結果になります。

Javaでは、「シグニチャ」で判定する

 Javaでは、どのメソッドを呼び出すのかを決定するために、メソッド名、パラメータの数とデータ型の並び、を見ます。これらの組み合わせ(「シグニチャ(signature)」といいます)が別であれば、異なるメソッドとして判定します。

 メソッドの判定において、戻り値型については使われません。スローされる例外のリストについても、メソッドの判定には利用されません。

System.out.printlnは実は……

 オーバーロードについて理解したところで、JavaのAPIでの例を見てみましょう。java.lang.Systemクラスを見ると分かりますが、実はSystem.outjava.io.PrintStream型で、printlnメソッドは複数宣言されています。つまり、オーバーロード機能を使って提供されています。

 このため、先ほどの例は次のように書けます。説明のために、わざわざ余計な処理を記述していたのでした。このほかにも、Java APIではオーバーロード機能を使ったメソッドがたくさん用意されています。

package sample18;
 
public class Sample03 {
    public static void main(String[] args) {
        System.out.println(1);
        System.out.println(2.0);
        System.out.println(false);
        System.out.println(new Object());
    }
}
sample18/Sample03.java

 オーバーロードを使うことで、パラメータが違うだけで同じ機能を提供する際に同名のメソッド名にできることは理解できたでしょうか。Java APIでもこの機能はよく使用されていますから、覚えておきましょう。

基本データ型を“包む”「ラッパークラス」とは

 次に、基本データ型の「ラッパークラス(wrapper class)」について説明します。基本データ型とは、boolean、char、byte、short、int、long、float、doubleといったデータ型のことをいいます。Javaでは、これらの基本データ型に対応するクラスを用意していて、それをラッパークラスといいます。具体的には、次の表をご覧ください。

基本データ型 ラッパークラス
boolean java.lang.Boolean
char java.lang.Character
byte java.lang.Byte
short java.lang.Short
int java.lang.Integer
long java.lang.Long
float java.lang.Float
double java.lang.Double
表 基本データ型とラッパークラスの対応

 オブジェクト指向言語を学ぶ側としては、プログラムの構成要素はオブジェクトであった方が分かりやすいはずです。しかし、処理効率からすると、基本データ型を使用した方が有利な場面が多いため、基本データ型で表現できる値は基本データ型として宣言することが多いはずです。とはいえ、これらの値をオブジェクトとして表現して扱いたい場合もあるので、ラッパークラスが用意されています。

 具体的には、Javaで用意されているコレクションフレームワーク(「java.util.Collectionインターフェイスなど)のように、オブジェクトの集合を対象とするAPIでは、基本データ型の値をそのまま使えないため、一度ラッパークラスを使ってオブジェクトにしてから、フレームワークでその値を処理することになります。

コラム 抽象クラス「Number」とラッパークラス「Void」

java.lang.Number」という抽象クラスもあります。数値用データ型のラッパークラスにおける共通機能について宣言しているクラスで、「Byte」「Short」「Integer」「Long」「Float」「Double」のスーパークラスです。

また、「java.lang.Void」というラッパークラスもあります。戻り値型がないことを示すためには、メソッドは「void メソッド名() {}」のように宣言する必要がありました。Voidクラスは、このときの「void」に対応するクラスです。通常は使うことがありませんから、そういうものがあるとだけ覚えておけばよいでしょう。


【データ型→ラッパーオブジェクト】valueOfメソッド

 ラッパークラスでは、valueOfメソッドがstatic宣言されていて、対応する基本データ型の値からラッパーオブジェクトを取得できます。「ラッパークラス.valueOf(値)」と書きます。

 また、文字列型の値からもラッパーオブジェクトを取得することもできます。「ラッパークラス.valueOf(【文字列型の値】)」と書きます。次のコードを見れば理解できるはずです。

    Boolean o1 = Boolean.valueOf(true);
    Boolean o2 = Boolean.valueOf("true");
    Integer o3 = Integer.valueOf(1);
    Integer o4 = Integer.valueOf("1");

【文字列型→データ型】parseで始まるメソッド

 文字列型の値を解析して基本データ型の値へ変換するメソッドもあります。「Boolean.parseBoolean("true")」とか「Integer.parseInt("10")」といった例を見れば分かるように、「ラッパークラス.parse型名(文字列型の値)」といったメソッドです。いずれも、static宣言されています。

 また、対応する基本データの最大値といった有用な定数が宣言されているので、簡単にそれらの値を参照できます。

 次のようなクラスを作成して実行してみましょう。

package sample18;
 
public class Sample04 {
    public static void main(String[] args) {
        boolean x1 = Boolean.parseBoolean("true");
        System.out.println(Boolean.toString(x1));
        int x2 = Integer.parseInt("10");
        System.out.println(Integer.toString(x2));
        // ラッパークラスがもつ定数の例
        System.out.println(Integer.MIN_VALUE); //最小値
        System.out.println(Integer.MAX_VALUE); //最大値
        System.out.println(Integer.SIZE); //サイズ
    }
}
sample18/Sample04.java

 実行結果は、次のようになります。

true
10
-2147483648
2147483647
32
Sample04.javaの実行結果

ラッパークラスには有用なメソッドがある

 このほかにも有用なメソッドがラッパークラスには宣言されています。基本データ型の値について何か変換したり調べたいことがある場合は、APIドキュメントなどで確認してみるとよいでしょう。

 次ページからは、型の互換性について説明します。型の互換性については、参照型と基本データ型について説明が必要です。まずは、参照型の互換性について説明します。

       1|2|3 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。