※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

5. 変換と昇格

Javaプログラミング言語で書かれた全ての式は、式の構造やその式の中に記述されたリテラルや変数やメソッドの型から推論できる型を持ちます。しかし、式の型が適切ではない文脈で式を書くこともできます。ある場合には、これはコンパイル時にエラーを引き起こします。他のケースでは、その式の型に関連する型を受理することができる文脈もあります。便宜上、プログラマーは明示的に型変換を要求する代わりに、Javaプログラミング言語は式の型をその周囲の文脈に合わせて受理することが可能な型に暗黙的に変換(conversion)を行います。

型Sから型Tへの特定の変換は型Sの式をあたかも型Tであるかのようにコンパイル時に扱えるようにします。変換の正当性を検査するために、または実行時の式の値を新たな型Tに対する適切な形式に変換するために、これは実行時に対応するアクションが必要になることがあります。

例5.0-1. コンパイル時と実行時の変換

  • Object型からThread型への変換は実行時の値が実際にThreadクラスまたはそのサブクラスのインスタンスかどうか確かめるための実行時検査が必要です。そうでなければ、例外がスローされます。
  • Thread型からObject型への変換は実行時のアクションを必要としません。ThreadObjectのサブクラスであるため、Thread型の式で生成されるいかなる参照もObject型の有効な参照値となります。
  • int型からlong型への変換は実行時に符号付き32ビット整数値の64ビット表現が必要とされます。情報は失われません。
  • double型からlong型への変換は64ビット浮動小数点値から64ビット整数表現への自明でない変換を必要とします。実際の実行時の値により、情報は失われるかもしれません。

どの変換文脈でも、特定の変換のみが許可されています。記述を簡便にするために、Javaプログラミング言語で可能な特定の変換をいくつかの広範囲なカテゴリーにグループ分けします。:
  • 恒等変換
  • 拡幅プリミティブ変換
  • 縮幅プリミティブ変換
  • 拡幅参照変換
  • 縮幅参照変換
  • ボックス化変換
  • ボックス化解除変換
  • 未検査変換
  • 捕捉変換
  • 文字列変換
  • 値集合変換

式の変換が起こる際に5つの変換文脈(conversion context)が存在します。どの文脈も上記に挙げたカテゴリーのいくつかの変換は許可し、その他は許可しません。"変換"という語はそのような文脈で特定の変換を選択する処理を記述するのにも使用します。例えば、メソッド呼び出し内の実引数である式を"メソッド呼び出し変換"と呼ぶことにします。これはメソッド呼び出し実引数文脈のための規則に従って暗黙的にその式に合った特定の変換が選択されるという意味です。

変換文脈の1つは+*のような数値演算子のオペランドです。そのようなオペランドに対す変換処理は数値昇格(numeric promotion)と呼ばれます。昇格は二項演算子の場合に特別で,一方のオペランドに対する変換の選択が,他方のオペランド式の型に部分的に依存する可能性があります。

本章ではまず初めに変換の11のカテゴリーについて記述します(5.1.)。そこには文字列連結演算子+に許されたStringへの特別な変換が含まれます。次に、5つの変換文脈について記述します。:
  代入変換はOutOfMemoryError(ボックス化変換の結果)やNullPointerException(ボックス化解除変換の結果)やClassCastException(未検査変換の結果)を実行時にスローする可能性があります。
  • メソッド呼び出し変換(15.9.15.12.)はメソッドやコンストラクター呼び出し内の各実引数に適用され、1つの例外を除いて、代入変換と同じ変換を行います。
  メソッド呼び出し変換はOutOfMemoryError(ボックス化変換の結果)やNullPointerException(ボックス化解除変換の結果)やClassCastException(未検査変換の結果)を実行時にスローする可能性があります。
  これは代入変換やメソッド呼び出し変換より包括的です。文字列変換以外の任意の特定変換が許可されます。しかし、参照型へのキャストは実行時に例外を生じる可能性があります。
  • 文字列変換は他方のオペランドがStringで自身がString以外の2項+演算子のオペランドにのみ適用されます。
  文字列変換はOutOfMemoryError(クラスインスタンス作成の結果)を実行時にスローする可能性があります。
  • 数値昇格は数値演算子のオペランドをその式を計算する共通の型に変換します。

例5.0-2. 変換文脈

class Test {			
    public static void main(String[] args) {
        // Casting conversion (5.4) of a float literal to
        // type int. Without the cast operator, this would
        // be a compile-time error, because this is a
        // narrowing conversion (5.1.3):
        int i = (int)12.5f;
        // String conversion (5.4) of i's int value:
        System.out.println("(int)12.5f==" + i);
        // Assignment conversion (5.2) of i's value to type
        // float. This is a widening conversion (5.1.2):
        float f = i;
        // String conversion of f's float value:
        System.out.println("after float widening: " + f);
        // Numeric promotion (5.6) of i's value to type
        // float. This is a binary numeric promotion.
        // After promotion, the operation is float*float:
        System.out.print(f);
        f = f * i;
        // Two string conversions of i and f:
        System.out.println("*" + i + "==" + f);
        // Method invocation conversion (5.3) of f's value
        // to type double, needed because the method Math.sin
        // accepts only a double argument:
        double d = Math.sin(f);
        // Two string conversions of f and d:
        System.out.println("Math.sin(" + f + ")==" + d);
    }
}

このプログラムは以下を出力します。:

(int)12.5f==12
after float widening: 12.0
12.0*12==144.0
Math.sin(144.0)==-0.49102159389846934

5.1. 変換の種類

5.2. 代入変換

5.3. メソッド呼び出し変換

5.4. 文字列変換

5.5. キャスト変換

5.6. 数値昇格