目次 | 前 | 次 | 索引 | Java言語規定 第2版 |
Java言語で記述されたすべての式は,その式の構造,並びに,その式の中に記述された,リテラル,変数及びメソッドの型から演繹できる型をもつ。しかし,式の型が適切でない式を書いてしまうこともできるので,コンパイル時にエラーとなる場合もある。
例えば,if
文(14.9)内の式がboolean
以外の型をもてば,コンパイル時エラーが発生する。文脈によっては,式の型が受理可能な場合もある。Java言語では,プログラマに明示的型変換を要求するかわりに,指定された式の型から,式をとりまく文脈にとって受理可能な型への暗黙的な 変換(conversion) を実行することを図る。
型 S から型 T への特定の変換によって,型 S の式を,あたかも型 T をもつかのようにコンパイル時に扱うことができる。この変換の正当性を検査する実行時動作,又は式の実行時の値を新しい型 T にとって適切な形式に翻訳する実行時動作が,必要となる場合もある。その例を次に示す。
Object
から型Thread
への変換は,実行時の値が実際にクラスThread
又はその下位クラスのインスタンスであることを確認するための実行時検査を要求する。そうでないときには例外が投げられる。
Thread
から型Object
への変換は,実行時動作を要求しない。Thread
はObject
の下位クラスなので,型Thread
の式が生成するいかなる参照も型Object
の正当な参照値となる。
int
から型long
への変換は,32ビット整数値の64ビットlong
表現への実行時符号拡張を要求する。いかなる情報も失われない。
double
から型long
への変換は,64ビット浮動小数点の値から64ビット整数表現への自明でない翻訳を要求する。実際の実行時の値によっては,情報が失われることがある。
すべての変換の文脈で,決められた特定の変換だけが許される。Javaプログラム言語で可能な特定の変換を,記述の便宜を図るために,次の大分類に類別する。
式の変換が発生可能な 変換の文脈(conversion context) には5種類ある。個々の文脈で,可能な変換を定めた。"変換" という用語は,文脈に対して特定の変換を選択する過程を記述するためにも使用する。例えば,メソッド呼出しで実引数の式が,"メソッド呼出し変換" に従う,というが,これはメソッド呼出し実引数の文脈に対する規則によって,その式に合った特定の変換を暗黙的に選択することを意味する。
変換文脈の中には,+
,*
などの数値演算子のオペランドとなる数値がある。この数値オペランドの変換過程を 数値昇格(numeric promotion) と呼ぶ。二項演算子の場合は,一方のオペランドに対する変換の選択が,他方のオペランド式の型に部分的に依存する可能性がある。
5.では,最初に,文字列の連結演算子+
に対して許されるString
への特殊な変換を含む,変換の七つの大分類(5.1)を規定する。次に,五つの変換文脈を規定する。
String
へと変換可能とする。
これは,次の出力を生成する。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
これは,二つの実用的な効果をもつ。第一に,すべての式は変換を受ける,と規則を簡潔に言明できる。第二に,明確化のために,プログラムが冗長なキャスト演算子を含むことを許可する。
型boolean
を含む唯一許される変換は,boolean
からboolean
への恒等変換とする。
byte
からshort
,int
,long
,float
,又はdouble
への変換。
short
からint
,long
,float
,又はdouble
への変換。
char
からint
,long
,float
,又はdouble
への変換。
int
からlong
,float
,又はdouble
への変換。
long
からfloat
又はdouble
への変換。
float
からdouble
への変換。
float
から型double
への拡大変換は,いかなる情報も失うことはない。つまり,数値を正確に保存する。
同じく,strictfp
式での型float
から型double
への拡大変換も,数値を正確に保存する。しかし,strictfp
でない変換は,変換後の数値の大きさについての情報を失うかもしれない。
int
若しくはlong
の値からfloat
への変換,又はlong
の値からdouble
への変換は,精度の損失(loss of precision),すなわち値の最小位の数ビットを失うことがある。この場合,浮動小数点の結果値は,IEEE 754直近へのまるめモード(4.2.4)を利用して,正しく丸めた整数値とする。
符号付き整数値の整数型 T への拡大変換は,単により長い桁を埋めるために整数値の2の補数表現について符号拡張をするだけとする。文字から整数型 T への拡大変換は,より長い桁を埋めるために文字値の表現をゼロ拡張する。
精度の損失が発生する可能性があるという事実にもかかわらず,プリミティブ型間の拡大変換は,実行時例外(11.)を生じない。
これは,次を印字する。class Test { public static void main(String[] args) { int big = 1234567890; float approx = big; System.out.println(big - (int)approx); } }
この例は,型-46
int
から型float
に変換する過程で,型float
の値が9桁の有効数字の精度がないために,情報が失われたことを示している。
byte
からchar
への変換。
short
からbyte
又はchar
への変換。
char
からbyte
又はshort
への変換。
int
からbyte
,short
又はchar
への変換。
long
からbyte
,short
,char
又はint
への変換。
float
からbyte
,short
,char
,int
又はlong
への変換。
double
からbyte
,short
,char
,int
,long
又はfloat
への変換。
符号付き整数の整数型 T への縮小変換は,単に下位nビット以外のすべての情報を捨てる。ここで,n は,型 T を表現するのに使用するビット数とする。数値の大きさに関する情報の損失の可能性に加えて,結果値の符号が,入力値の符号と異なってしまうことがある。
同様にして,文字型から整数型 T への縮小変換は,単に下位nビット以外のすべての情報を捨てる。ここで n は,型 T を表現するのに使用するビット数とする。数値の大きさに関する情報の損失の可能性に加えて,文字が16ビットの符号なし整数値を表わすにもかかわらず,結果値が,負の数になることがある。
浮動小数点数から整数型 T への縮小変換は,二つの段階を踏む。
long
ならばlong
に変換し,T がbyte
,short
,char
又はint
ならばint
に変換する。
int
又はlong
の0
とする。
int
又はlong
ならば,変換の結果は,第一段階の結果とする。
byte
,char
又はshort
ならば,変換の結果は,第一段階の結果の型 T への縮小変換(5.1.3)の結果とする。
これは,次を出力する。class Test { public static void main(String[] args) { float fmin = Float.NEGATIVE_INFINITY; float fmax = Float.POSITIVE_INFINITY; System.out.println("long: " + (long)fmin + ".." + (long)fmax); System.out.println("int: " + (int)fmin + ".." + (int)fmax); System.out.println("short: " + (short)fmin + ".." + (short)fmax); System.out.println("char: " + (int)(char)fmin + ".." + (int)(char)fmax); System.out.println("byte: " + (byte)fmin + ".." + (byte)fmax); } }
long: -9223372036854775808..9223372036854775807 int: -2147483648..2147483647 short: 0..-1 char: 0..65535 byte: 0..-1
char
,int
及びlong
に対する結果は,その型の最小及び最大表現可能値を生成する。
byte
及びshort
に対する結果は,符号及び数値の大きさに関する情報を失い,精度も失う。
この結果は,int
の最小数及び最大数の下位ビットを調べることにより理解できる。
最小のint
は16進で0x80000000
であって,最大のint
は0x7fffffff
である。
これから,short
の結果が値の下位16ビット,つまり0x0000
及び0xffff
となることが説明される。
またchar
の結果もまた,これらの値の下位16ビット,つまり'\u0000'
及び'\uffff'
となることで説明される。更にbyte
の結果が,これらの値の下位8ビット,つまり0x00
及び0xff
となることで説明される。
オーバフロー,アンダフロー又はその他の情報の損失が生ずる可能性があるという事実にもかかわらず,プリミティブ型間の縮小変換は,実行時例外(11.)を生じない。
情報を損失する多くの縮小変換を例示する,小さなテストプログラムを次に示す。
このテストプログラムは次の出力を生成する。class Test { public static void main(String[] args) { // A narrowing of int to short loses high bits: System.out.println("(short)0x12345678==0x" + Integer.toHexString((short)0x12345678)); // A int value not fitting in byte changes sign and magnitude: System.out.println("(byte)255==" + (byte)255); // A float value too big to fit gives largest int value: System.out.println("(int)1e20f==" + (int)1e20f); // A NaN converted to int yields zero: System.out.println("(int)NaN==" + (int)Float.NaN); // A double value too large for float yields infinity: System.out.println("(float)-1e100==" + (float)-1e100); // A double value too small for float underflows to zero: System.out.println("(float)1e-50==" + (float)1e-50); } }
(short)0x12345678==0x5678 (byte)255==-1 (int)1e20f==2147483647 (int)NaN==0 (float)-1e100==-Infinity (float)1e-50==0.0
Object
への拡大変換が存在する)。
Object
への変換。
Object
への変換。
Cloneable
への変換。
java.io.Serializable
への変換。
[]
から任意の TC[]
への変換。
クラスの詳細な規定については8.を,インタフェースについては9.を,配列については10.を参照のこと。
Object
から任意の他のクラス型への縮小変換が存在する。)
Object
から任意のインタフェース型への縮小変換が存在する。)
Object
から任意の配列型への変換。
Object
から任意のインタフェース型への変換。
final
でない任意のクラス型 T への変換。
final
な任意のクラス型 T への変換。ただし,T は J を実装する。
[]
から任意の配列型 TC []
への変換。ただし,SC 及び TC は参照型とし,SC から TC への縮小変換が存在する。
ClassCastException
が投げられる。String
への文字列変換が存在する。boolean
への変換は許さない。
boolean
からの変換は許さない。
final
なクラス型 Sが,インタフェース型 K を実装しなければ,S から K への変換は許さない。
Object
でなければ,S から任意の配列型への変換は許さない。
final
なクラス型 Tが,インタフェース型 J を実装しなければ,文字列変換以外のJ から T への変換は許さない。
Object
又はString
以外の任意のクラス型への任意の配列型からの変換は許さない。
java.io.Serializable
とCloneable
への変換を除いて許さない。
[]
から配列型 TC []
への変換は許さない。
FP厳密(FP-strict)(15.4)でない式中では,値集合変換はJavaプログラム言語の実装に選択肢をもたせる。
float
で,単精度数値集合の要素でない場合,実装はその値を単精度数値集合の中の最も近い要素に写像しなければならない。
この変換は,オーバフロー又はアンダフローするかもしれない。
double
で,倍精度数値集合の要素でない場合,実装はその値を倍精度数値集合の中の最も近い要素に写像しなければならない。
この変換は,オーバフロー又はアンダフローするかもしれない。
FP厳密なコードである無いにかかわらず,値集合変換は常にfloat
又はdouble
以外の型の値を変えない。
変数の型がfloat
又はdouble
の場合,値集合変換は型変換の後に適用される。
float
で,単精度指数部拡張数値集合の要素の場合,実装は単精度数値集合の中で最も近い要素に値を写像しなければならない。
この変換は,オーバフロー又はアンダフローするかもしれない。
double
で,倍精度指数部拡張数値集合の要素の場合,実装は倍精度数値集合の中で最も近い要素に値を写像しなければならない。
この変換は,オーバフロー又はアンダフローするかもしれない。
コンパイル時の定数の縮小とは,次のようなコードを許すことを意味する。
縮小がなければ,整数リテラルbyte theAnswer = 42;
42
は型int
をもつという事実は,byte
へのキャストを要求することになる。
プリミティブ型の値は,参照型の変数に代入してはならない。これを実行しようとすると,コンパイル時エラーを生じる。型byte theAnswer = (byte)42; // cast is permitted but not required
boolean
の値は,型boolean
の変数にのみ代入できる。次のテストプログラムは,プリミティブ型の値の代入変換に関する例を含む。
このプログラムは,次の出力を生成する。class Test { public static void main(String[] args) { short s = 12; // narrow 12 to short float f = s; // widen short to float System.out.println("f=" + f); char c = '\u0123'; long l = c; // widen char to long System.out.println("l=0x" + Long.toString(l,16)); f = 1.23f; double d = f; // widen float to double System.out.println("d=" + d); } }
しかし,次のテストプログラムは,コンパイル時エラーとなる。f=12.0 l=0x123 d=1.2300000190734863
その理由は,すべてのclass Test { public static void main(String[] args) { short s = 123; char c = s; // error: would require cast s = c; // error: would require cast } }
short
値がchar
値ではないし,すべてのchar
値がshort
値でもないからとする。空型の値(空参照が唯一その値)は,あらゆる参照型に代入可能で,その結果は,その型の空参照とする。
コンパイル時参照型 S(代入元)の値のコンパイル時参照型 T(代入先)への代入は,次のとおり検査する。public class Point { int x, y; } public class Point3D extends Point { int z; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } class Test { public static void main(String[] args) { // Assignments to variables of class type: Point p = new Point(); p = new Point3D(); // ok: because Point3D is a // subclass of Point Point3D p3d = p; // error: will require a cast because a // Point might not be a Point3D // (even though it is, dynamically, // in this example.) // Assignments to variables of type Object: Object o = p; // ok: any object to Object int[] a = new int[3]; Object o2 = a; // ok: an array to Object // Assignments to variables of interface type: ColoredPoint cp = new ColoredPoint(); Colorable c = cp; // ok: ColoredPoint implements // Colorable // Assignments to variables of array type: byte[] b = new byte[4]; a = b; // error: these are not arrays // of the same primitive type Point3D[] p3da = new Point3D[3]; Point[] pa = p3da; // ok: since we can assign a // Point3D to a Point p3da = pa; // error: (cast needed) since a Point // can't be assigned to a Point3D } }
Object
でなければならない。そうでないときには,コンパイル時エラーが発生する。
[]
,つまり型 SC の構成要素をもつ配列の場合。
配列オブジェクトの代入を含む例をもう一つ次に示す。public class Point { int x, y; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } class Test { public static void main(String[] args) { Point p = new Point(); ColoredPoint cp = new ColoredPoint(); // Okay because ColoredPoint is a subclass of Point: p = cp; // Okay because ColoredPoint implements Colorable: Colorable c = cp; // The following cause compile-time errors because // we cannot be sure they will succeed, depending on // the run-time type of p; a run-time check will be // necessary for the needed narrowing conversion and // must be indicated by including a cast: cp = p; // p might be neither a ColoredPoint // nor a subclass of ColoredPoint c = p; // p might not implement Colorable } }
この例では,class Point { int x, y; } class ColoredPoint extends Point { int color; } class Test { public static void main(String[] args) { long[] veclong = new long[100]; Object o = veclong; // okay Long l = veclong; // compile-time error short[] vecshort = veclong; // compile-time error Point[] pvec = new Point[100]; ColoredPoint[] cpvec = new ColoredPoint[100]; pvec = cpvec; // okay pvec[0] = new Point(); // okay at compile time, // but would throw an // exception at run time cpvec = pvec; // compile-time error } }
Long
はObject
以外のクラス型だから,veclong
の値をLong
の変数に代入することはできない。配列は,互換性のある配列型の変数又は型Object
の変数にだけ代入できる。
veclong
の値は,プリミティブ型の配列であって,short
及びlong
は同じプリミティブ型ではないから,vecshort
には代入できない。
ColoredPoint
の式の値となることが可能な任意の参照は,型Point
の変数の値となることが可能だから,cpvec
の値はpvec
に代入できる。
それに続く,新たなPoint
のpvec
の構成要素への代入は,(このプログラムをコンパイルできるように別途修正したとしても,)例外ArrayStoreException
を投げる。その理由は,配列ColoredPoint
は,構成要素の値として型Point
のインスタンスをもつことができないからとなる。
ColoredPoint
の式の値となることが可能な参照が必ずしもすべて型Point
の変数の正しい値であるとは限らないから,pvec
の値は,cpvec
に代入できない。pvec
の実行時の値がPoint[]
のインスタンスへの参照であって,cpvec
への代入が許されていれば,cpvec
の構成要素への単純参照,例えばcpvec[0]
はPoint
を返すことができるが,Point
はColoredPoint
ではない。したがって,そのような代入を許すことは,型システムの破壊を許すことになる。
pvec
がColoredPoint[]
を参照するのを保証するために,キャストを使用することができる(5.5,15.16)。
cpvec = (ColoredPoint[])pvec; // okay, but may throw an // exception at run time
実引数式の型がfloat
もしくはdouble
ならば,型変換の後に,値集合変換 (5.1.8) が適用される。
float
の実引数値が単精度指数部拡張数値集合の要素ならば,実装はその値を最も近いfloat値集合の要素に対応させなければならない。この変換によってオーバフロー,アンダフローが発生することがある。
double
の実引数値が倍精度指数部拡張数値集合の要素ならば,実装はその値に最も近いdouble値集合に対応させなければならない。この変換によってオーバフロー,アンダフローが発生することがある。
これは変数リテラルclass Test { static int m(byte a, int b) { return a+b; } static int m(short a, short b) { return a-b; } public static void main(String[] args) { System.out.println(m(12, 2)); // compile-time error } }
12
及び2
は,型int
を持つのでいずれのメソッドm
も(15.12.2)の規則のもとでは一致しないからである。整数定数の暗黙縮小を含む言語では,この例のような場合を解決する追加規則を必要とする。String
のとき,二項演算子 +
のオペランドにだけ適用する。この場合,+
の他方の実引数を String
に変換し,二つの文字列の連結である新しい String
が +
の結果となる。文字列変換は,文字列の連結演算子 +
(15.18.1)の記述で詳細に規定する。値集合変換(5.1.8)は,型変換の後に適用される。
コンパイル時に不正と証明されるキャストもある。このようなキャストは,コンパイル時エラーを発生させる。
プリミティブ型の値は,型が同じならば恒等変換によって,そうでないときにはプリミティブ型の拡大変換又はプリミティブ型の縮小変換によって,他のプリミティブ型にキャストできる。
プリミティブ型の値を,キャスト変換によって参照型にキャストすることはできないし,参照型の値をプリミティブ型にキャストすることもできない。
残りの場合は,参照型間の変換に関係する。コンパイル時参照型 S (変換元)の値から,コンパイル時参照型 T (変換先)へのキャスト変換のコンパイル時正当性検査に関する詳細規則は次のとおりとする。
final
クラス(8.1.1)でなければ,キャストは,コンパイル時には常に正しい (なぜなら,S が T を実装していなくとも,S の下位クラスが実装しているかもしれないからである)。
final
クラス(8.1.1)ならば,S は T を実装しなければならない。さもなければ,コンパイル時エラーが発生する。
Object
でなければならない。さもなければ,コンパイル時エラーが発生する。
final
(8.1.1)でないクラス型ならば,キャストはコンパイル時には常に正しい(なぜなら T が S を実装していなくとも,T の下位クラスが実装しているかもしれないからである)。
[]
,つまり型 SCを構成要素に持つ配列の場合。
参照型へのキャストがコンパイル時エラーでなければ,次の二つの場合が存在する。
null
ならば,キャストは可能となる。それ以外の場合は,R を実行時の参照値によって参照されるオブジェクトのクラスとし,T をそのキャスト演算子によって名前を与えられた型とする。キャスト変換は,実行時にクラス R が型 T と代入互換であることを
5.2で規定されたアルゴリズムをそこで規定しているコンパイル時の型 S をクラス R に置き換えて使用して,検査しなければならない(これらの規則を任意の与えられたキャストに対して最初に適用するときには,R はインタフェースであってはならないが,これらの規則を再帰的に適用すれば,R はインタフェースになることがある点に注意すること。その理由は,実行時の参照値が,その要素の型がインタフェース型である配列を参照することがあるからである)。この修正されたアルゴリズムを次に示す。
Object
(4.3.2)でなければならない。さもなければ,実行時例外が投げられる。
[]
を表わすクラス,つまり RC を構成要素とする配列の場合。
Object
(4.3.2)でなければならない。さもなければ,実行時例外が投げられる。
java.io.Serializable
もしくは Cloneable
でなければ,実行時例外が投げられる(この場合は,例えば配列への参照が型 Object
の変数に記憶されるときには,コンパイル時検査をすり抜ける可能性がある)。
[]
つまり型 TC を構成要素とする配列のとき,次の一つが真でなければ,実行時例外が投げられる。
ClassCastException
とする。5.2の例と同様の参照型へのキャスト変換の例を次に示す。
ここで,クラス型public class Point { int x, y; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } final class EndPoint extends Point { } class Test { public static void main(String[] args) { Point p = new Point(); ColoredPoint cp = new ColoredPoint(); Colorable c; // The following may cause errors at run time because // we cannot be sure they will succeed; this possibility // is suggested by the casts: cp = (ColoredPoint)p; // p might not reference an // object which is a ColoredPoint // or a subclass of ColoredPoint c = (Colorable)p; // p might not be Colorable // The following are incorrect at compile time because // they can never succeed as explained in the text: Long l = (Long)p; // compile-time error #1 EndPoint e = new EndPoint(); c = (Colorable)e; // compile-time error #2 } }
Long
及び Point
は関係しない(つまり,これらは同じではないし,相手の下位クラスでもない)ために,最初のコンパイル時エラーが生じる。従って,これらの間のキャストは,常に失敗する。
型 EndPoint
の変数はインタフェース Colorable
を実装する値を参照できないために,第二のコンパイル時エラーを生じる。これは,EndPoint
が final
な型であって,final
な型の変数は常にコンパイル時の型と同じ実行時の型の値を保持することによる。そこで,変数 e
の実行時の型は正確に型 EndPoint
でなければならず,型 EndPoint
は Colorable
を実装しない。
次に,配列(10.)を含む例を示す。
この例は,エラーなしにコンパイルされ,次の出力を生成する。class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } public String toString() { return "("+x+","+y+")"; } } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; ColoredPoint(int x, int y, int color) { super(x, y); setColor(color); } public void setColor(int color) { this.color = color; } public String toString() { return super.toString() + "@" + color; } } class Test { public static void main(String[] args) { Point[] pa = new ColoredPoint[4]; pa[0] = new ColoredPoint(2, 2, 12); pa[1] = new ColoredPoint(4, 5, 24); ColoredPoint[] cpa = (ColoredPoint[])pa; System.out.print("cpa: {"); for (int i = 0; i < cpa.length; i++) System.out.print((i == 0 ? " " : ", ") + cpa[i]); System.out.println(" }"); } }
次の例は,コンパイルのためにキャストを使用しているが,型に互換性がないために実行時に例外を投げる。cpa: { (2,2)@12, (4,5)@24, null, null }
public class Point { int x, y; } public interface Colorable { void setColor(int color); } public class ColoredPoint extends Point implements Colorable { int color; public void setColor(int color) { this.color = color; } } class Test { public static void main(String[] args) { Point[] pa = new Point[100]; // The following line will throw a ClassCastException: ColoredPoint[] cpa = (ColoredPoint[])pa; System.out.println(cpa[0]); int[] shortvec = new int[2]; Object o = shortvec; // The following line will throw a ClassCastException: Colorable c = (Colorable)o; c.setColor(0); } }
数値昇格は,数値演算子のオペランドを共通の型に変換するために使用する。演算が実行可能となるように,数値昇格には,単項数値昇格(5.6.1)と二項数値昇格(5.6.2)の二種類が存在する。C言語での類似の変換は,"通常の単項変換" 及び "通常の二項変換"と呼ぶ。
数値昇格は,Javaプログラム言語の全体的な特徴ではなく,組込み演算の特性である。
byte
,short
,又は char
を持てば,単項数値昇格はこれを拡大変換(5.1.2)によって型 int
の値に昇格する。
+
(15.15.3)。
-
(15.15.4)。
~
(15.15.5)。
>>
,>>>
,及び <<
(15.19),それぞれの各オペランド。それゆえ,シフト幅(右オペランド)が型 long
でも,シフトされる値(左オペランド)は型 long
に昇格しない。
単項数値昇格の例を含むテストプログラムを次に示す。
このテストプログラムは,次の出力を生成する。class Test { public static void main(String[] args) { byte b = 2; int a[] = new int[b]; // dimension expression promotion char c = '\u0001'; a[c] = 1; // index expression promotion a[0] = -c; // unary - promotion System.out.println("a: " + a[0] + "," + a[1]); b = -1; int i = ~b; // bitwise complement promotion System.out.println("~0x" + Integer.toHexString(b) + "==0x" + Integer.toHexString(i)); i = b << 4L; // shift promotion (left operand) System.out.println("0x" + Integer.toHexString(b) + "<<4L==0x" + Integer.toHexString(i)); } }
a: -1,1 ~0xffffffff==0x0 0xffffffff<<4L==0xfffffff0
double
ならば,他方を double
に変換する。
float
ならば,他方を float
に変換する。
long
ならば,他方を long
に変換する。
int
に変換する。
*
,/
及び %
(15.17)。
+
及び減算演算子 -
(15.18.2)。
<
,<=
,>
及び >=
(15.20.1)。
==
及び !=
(15.21.1)。
&
,^
及び |
(15.22.1)。
? :
(15.25)。
二項数値昇格の例は,上記の5.1に存在する。ここでは別の例を示す。
これは次の出力を生成する。class Test { public static void main(String[] args) { int i = 0; float f = 1.0f; double d = 2.0; // First int*float is promoted to float*float, then // float==double is promoted to double==double: if (i * f == d) System.out.println("oops"); // A char&byte is promoted to int&int: byte b = 0x1f; char c = 'G'; int control = c & b; System.out.println(Integer.toHexString(control)); // Here int:float is promoted to float:float: f = (b==0) ? i : 4.0f; System.out.println(1.0/f); } }
この例は,ASCII文字7 0.25
G
の下位5ビットを除く全体にゼロのマスクをかけることによって,この文字をASCII文字の control-G (BEL)に変換する。7
は,この制御文字の数値である。
目次 | 前 | 次 | 索引 | Java言語規定 第2版 |