| 目次 | 前 | 次 | 索引 | 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値でもないからとする。空型の値(空参照が唯一その値)は,あらゆる参照型に代入可能で,その結果は,その型の空参照とする。
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
}
}
コンパイル時参照型 S(代入元)の値のコンパイル時参照型 T(代入先)への代入は,次のとおり検査する。
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版 |