-
Notifications
You must be signed in to change notification settings - Fork 0
/
chapter-5.txt
1378 lines (817 loc) · 69 KB
/
chapter-5.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Chapter 5. Conversions and Contexts
Table of Contents
5.1. Kinds of Conversion
5.1.1. Identity Conversion
5.1.2. Widening Primitive Conversion
5.1.3. Narrowing Primitive Conversion
5.1.4. Widening and Narrowing Primitive Conversion
5.1.5. Widening Reference Conversion
5.1.6. Narrowing Reference Conversion
5.1.6.1. Allowed Narrowing Reference Conversion
5.1.6.2. Checked and Unchecked Narrowing Reference Conversions
5.1.6.3. Narrowing Reference Conversions at Run Time
5.1.7. Boxing Conversion
5.1.8. Unboxing Conversion
5.1.9. Unchecked Conversion
5.1.10. Capture Conversion
5.1.11. String Conversion
5.1.12. Forbidden Conversions
5.2. Assignment Contexts
5.3. Invocation Contexts
5.4. String Contexts
5.5. Casting Contexts
5.6. Numeric Contexts
5.7. Testing Contexts
Every expression written in the Java programming language either produces no result (§15.1) or has a type that can be deduced at compile time (§15.3). When an expression appears in most contexts, it must be compatible with a type expected in that context; this type is called the target type. For convenience, compatibility of an expression with its surrounding context is facilitated in two ways:
First, for some expressions, termed poly expressions (§15.2), the deduced type can be influenced by the target type. The same expression can have different types in different contexts.
Second, after the type of the expression has been deduced, an implicit conversion from the type of the expression to the target type can sometimes be performed.
If neither strategy is able to produce the appropriate type, a compile-time error occurs.
The rules determining whether an expression is a poly expression, and if so, its type and compatibility in a particular context, vary depending on the kind of context and the form of the expression. In addition to influencing the type of the expression, the target type may in some cases influence the run time behavior of the expression in order to produce a value of the appropriate type.
Similarly, the rules determining whether a target type allows an implicit conversion vary depending on the kind of context, the type of the expression, and, in one special case, the value of a constant expression (§15.29). A conversion from type S to type T allows an expression of type S to be treated at compile time as if it had type T instead. In some cases this will require a corresponding action at run time to check the validity of the conversion or to translate the run-time value of the expression into a form appropriate for the new type T.
Example 5.0-1. Conversions at Compile Time and Run Time
A conversion from type Object to type Thread requires a run-time check to make sure that the run-time value is actually an instance of class Thread or one of its subclasses; if it is not, an exception is thrown.
A conversion from type Thread to type Object requires no run-time action; Thread is a subclass of Object, so any reference produced by an expression of type Thread is a valid reference value of type Object.
A conversion from type int to type long requires run-time sign-extension of a 32-bit integer value to the 64-bit long representation. No information is lost.
A conversion from type double to type long requires a non-trivial translation from a 64-bit floating-point value to the 64-bit integer representation. Depending on the actual run-time value, information may be lost.
The conversions possible in the Java programming language are grouped into several broad categories:
Identity conversions
Widening primitive conversions
Narrowing primitive conversions
Widening reference conversions
Narrowing reference conversions
Boxing conversions
Unboxing conversions
Unchecked conversions
Capture conversions
String conversions
There are seven kinds of conversion contexts in which poly expressions may be influenced by context or implicit conversions may occur. Each kind of context has different rules for poly expression typing and allows conversions in some of the categories above but not others. The contexts are:
Assignment contexts (§5.2, §15.26), in which an expression's value is bound to a named variable. Primitive and reference types are subject to widening, values may be boxed or unboxed, and some primitive constant expressions may be subject to narrowing. An unchecked conversion may also occur.
Strict invocation contexts (§5.3, §15.9, §15.12), in which an argument is bound to a formal parameter of a constructor or method. Widening primitive, widening reference, and unchecked conversions may occur.
Loose invocation contexts (§5.3, §15.9, §15.12), in which, like strict invocation contexts, an argument is bound to a formal parameter. Method or constructor invocations may provide this context if no applicable declaration can be found using only strict invocation contexts. In addition to widening and unchecked conversions, this context allows boxing and unboxing conversions to occur.
String contexts (§5.4, §15.18.1), in which a value of any type is converted to an object of type String.
Casting contexts (§5.5), in which an expression's value is converted to a type explicitly specified by a cast operator (§15.16). Casting contexts are more inclusive than assignment or loose invocation contexts, allowing any specific conversion other than a string conversion, but certain casts to a reference type are checked for correctness at run time.
Numeric contexts (§5.6), in which the operands of a numeric operator or some other expressions that operate on numbers may be widened to a common type.
Testing contexts (§5.7), in which an expression's value is converted to a type explicitly specified by a pattern (§14.30). Testing contexts are more inclusive than assignment or loose invocation contexts, but not as inclusive as casting contexts.
The term "conversion" is also used to describe, without being specific, any conversions allowed in a particular context. For example, we say that an expression that is the initializer of a local variable is subject to "assignment conversion", meaning that a specific conversion will be implicitly chosen for that expression according to the rules for the assignment context. As another example, we say that an expression undergoes "casting conversion" to mean that the expression's type will be converted as permitted in a casting context.
Example 5.0-2. Conversions In Various Contexts
class Test {
public static void main(String[] args) {
// Casting conversion (5.5) 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);
// 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);
}
}
This program produces the output:
(int)12.5f==12
after float widening: 12.0
12.0*12==144.0
Math.sin(144.0)==-0.49102159389846934
5.1. Kinds of Conversion
Specific type conversions in the Java programming language are divided into 12 kinds.
5.1.1. Identity Conversion
A conversion from a type to that same type is permitted for any type.
This may seem trivial, but it has two practical consequences. First, it is always permitted for an expression to have the desired type to begin with, thus allowing the simply stated rule that every expression is subject to conversion, if only a trivial identity conversion. Second, it implies that it is permitted for a program to include redundant cast operators for the sake of clarity.
5.1.2. Widening Primitive Conversion
19 specific conversions on primitive types are called the widening primitive conversions:
byte to short, int, long, float, or double
short to int, long, float, or double
char to int, long, float, or double
int to long, float, or double
long to float or double
float to double
A widening primitive conversion does not lose information about the overall magnitude of a numeric value in the following cases, where the numeric value is preserved exactly:
from an integral type to another integral type
from byte, short, or char to a floating-point type
from int to double
from float to double
A widening primitive conversion from int to float, or from long to float, or from long to double, may result in loss of precision, that is, the result may lose some of the least significant bits of the value. In this case, the resulting floating-point value will be a correctly rounded version of the integer value, using the round to nearest rounding policy (§15.4).
A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format.
A widening conversion of a char to an integral type T zero-extends the representation of the char value to fill the wider format.
A widening conversion from int to float, or from long to float, or from int to double, or from long to double occurs as determined by the rules of IEEE 754 for converting from an integer format to a binary floating-point format.
A widening conversion from float to double occurs as determined by the rules of IEEE 754 for converting between binary floating-point formats.
Despite the fact that loss of precision may occur, a widening primitive conversion never results in a run-time exception (§11.1.1).
Example 5.1.2-1. Widening Primitive Conversion
class Test {
public static void main(String[] args) {
int big = 1234567890;
float approx = big;
System.out.println(big - (int)approx);
}
}
This program prints:
-46
thus indicating that information was lost during the conversion from type int to type float because values of type float are not precise to nine significant digits.
5.1.3. Narrowing Primitive Conversion
22 specific conversions on primitive types are called the narrowing primitive conversions:
short to byte or char
char to byte or short
int to byte, short, or char
long to byte, short, char, or int
float to byte, short, char, int, or long
double to byte, short, char, int, long, or float
A narrowing primitive conversion may lose information about the overall magnitude of a numeric value, and may also lose precision and range.
A narrowing conversion of a signed integer to an integral type T simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the sign of the resulting value to differ from the sign of the input value.
A narrowing conversion of a char to an integral type T likewise simply discards all but the n lowest order bits, where n is the number of bits used to represent type T. In addition to a possible loss of information about the magnitude of the numeric value, this may cause the resulting value to be a negative number, even though chars represent 16-bit unsigned integer values.
A narrowing conversion of a floating-point number to an integral type T takes two steps:
In the first step, the floating-point number is converted either to a long, if T is long, or to an int, if T is byte, short, char, or int, as follows:
If the floating-point number is NaN (§4.2.3), the result of the first step of the conversion is an int or long 0.
Otherwise, if the floating-point number is not an infinity, the floating-point value is rounded to an integer value V using the round toward zero rounding policy (§4.2.4). Then there are two cases:
If T is long, and this integer value can be represented as a long, then the result of the first step is the long value V.
Otherwise, if this integer value can be represented as an int, then the result of the first step is the int value V.
Otherwise, one of the following two cases must be true:
The value must be too small (a negative value of large magnitude or negative infinity), and the result of the first step is the smallest representable value of type int or long.
The value must be too large (a positive value of large magnitude or positive infinity), and the result of the first step is the largest representable value of type int or long.
In the second step:
If T is int or long, the result of the conversion is the result of the first step.
If T is byte, char, or short, the result of the conversion is the result of a narrowing conversion to type T (§5.1.3) of the result of the first step.
A narrowing conversion from double to float occurs as determined by the rules of IEEE 754 for converting between binary floating-point formats, using the round to nearest rounding policy (§15.4). This conversion can lose precision, but also lose range, resulting in a float zero from a nonzero double and a float infinity from a finite double. A double NaN is converted to a float NaN and a double infinity is converted to the same-signed float infinity.
Despite the fact that overflow, underflow, or other loss of information may occur, a narrowing primitive conversion never results in a run-time exception (§11.1.1).
Example 5.1.3-1. Narrowing Primitive Conversion
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);
}
}
This program produces the output:
long: -9223372036854775808..9223372036854775807
int: -2147483648..2147483647
short: 0..-1
char: 0..65535
byte: 0..-1
The results for char, int, and long are unsurprising, producing the minimum and maximum representable values of the type.
The results for byte and short lose information about the sign and magnitude of the numeric values and also lose precision. The results can be understood by examining the low order bits of the minimum and maximum int. The minimum int is, in hexadecimal, 0x80000000, and the maximum int is 0x7fffffff. This explains the short results, which are the low 16 bits of these values, namely, 0x0000 and 0xffff; it explains the char results, which also are the low 16 bits of these values, namely, '\u0000' and '\uffff'; and it explains the byte results, which are the low 8 bits of these values, namely, 0x00 and 0xff.
Example 5.1.3-2. Narrowing Primitive Conversions that lose information
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));
// An int value too big for 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);
}
}
This program produces the output:
(short)0x12345678==0x5678
(byte)255==-1
(int)1e20f==2147483647
(int)NaN==0
(float)-1e100==-Infinity
(float)1e-50==0.0
5.1.4. Widening and Narrowing Primitive Conversion
The following conversion combines both widening and narrowing primitive conversions:
byte to char
First, the byte is converted to an int via widening primitive conversion (§5.1.2), and then the resulting int is converted to a char by narrowing primitive conversion (§5.1.3).
5.1.5. Widening Reference Conversion
A widening reference conversion exists from any reference type S to any reference type T, provided S is a subtype of T (§4.10).
Widening reference conversions never require a special action at run time and therefore never throw an exception at run time. They consist simply in regarding a reference as having some other type in a manner that can be proved correct at compile time.
The null type is not a reference type (§4.1), and so a widening reference conversion does not exist from the null type to a reference type. However, many conversion contexts explicitly allow the null type to be converted to a reference type.
5.1.6. Narrowing Reference Conversion
A narrowing reference conversion treats expressions of a reference type S as expressions of a different reference type T, where S is not a subtype of T. The supported pairs of types are defined in §5.1.6.1. Unlike widening reference conversion, the types need not be directly related. However, there are restrictions that prohibit conversion between certain pairs of types when it can be statically proven that no value can be of both types.
A narrowing reference conversion may require a test at run time to validate that a value of type S is a legitimate value of type T. However, due to the lack of parameterized type information at run time, some conversions cannot be fully validated by a run time test; they are flagged at compile time (§5.1.6.2). For conversions that can be fully validated by a run time test, and for certain conversions that involve parameterized type information but can still be partially validated at run time, a ClassCastException is thrown if the test fails (§5.1.6.3).
5.1.6.1. Allowed Narrowing Reference Conversion
A narrowing reference conversion exists from reference type S to reference type T if all of the following are true:
S is not a subtype of T (§4.10)
If there exists a parameterized type X that is a supertype of T, and a parameterized type Y that is a supertype of S, such that the erasures of X and Y are the same, then X and Y are not provably distinct (§4.5).
Using types from the java.util package as an example, no narrowing reference conversion exists from ArrayList<String> to ArrayList<Object>, or vice versa, because the type arguments String and Object are provably distinct. For the same reason, no narrowing reference conversion exists from ArrayList<String> to List<Object>, or vice versa. The rejection of provably distinct types is a simple static gate to prevent "stupid" narrowing reference conversions.
One of the following cases applies:
S is a class or interface type, and T is a class or interface type, and S names a class or interface that is not disjoint from the class or interface named by T. ("disjoint" is defined below.)
S is the class type Object or the interface type java.io.Serializable or Cloneable (the only interfaces implemented by arrays (§10.8)), and T is an array type.
S is an array type SC[], that is, an array of components of type SC; T is an array type TC[], that is, an array of components of type TC; and a narrowing reference conversion exists from SC to TC.
S is a type variable, and a narrowing reference conversion exists from the upper bound of S to T.
T is a type variable, and either a widening reference conversion or a narrowing reference conversion exists from S to the upper bound of T.
S is an intersection type S1 & ... & Sn, and for all i (1 ≤ i ≤ n), either a widening reference conversion or a narrowing reference conversion exists from Si to T.
T is an intersection type T1 & ... & Tn, and for all i (1 ≤ i ≤ n), either a widening reference conversion or a narrowing reference conversion exists from S to Ti.
A class or interface is disjoint from another class or interface if it can be determined statically that they have no instances in common (other than the null value). The rules for disjointess are as follows:
A class named C is disjoint from an interface named I if (i) it is not the case that C <: I, and (ii) one of the following cases applies:
C is final.
C is sealed, and all of the permitted direct subclasses of C are disjoint from I.
C is freely extensible (§8.1.1.2), and I is sealed, and C is disjoint from all of the permitted direct subclasses and subinterfaces of I.
An interface named I is disjoint from a class named C if C is disjoint from I.
A class named C is disjoint from another class named D if (i) it is not the case that C <: D, and (ii) it is not the case that D <: C.
An interface named I is disjoint from another interface named J if (i) it is not that case that I <: J, and (ii) it is not the case that J <: I, and (iii) one of the following cases applies:
I is sealed, and all of the permitted direct subclasses and subinterfaces of I are disjoint from J.
J is sealed, and I is disjoint from all the permitted direct subclasses and subinterfaces of J.
Whether a class is final has the most bearing on whether the class is disjoint from interfaces. Consider the following declarations:
interface I {}
final class C {}
As class C is final and does not implement I, there can be no instances of C that are also an instance of I, so C and I are disjoint. Therefore, there is no narrowing reference conversion from C to I.
In contrast, consider the following declarations:
interface J {}
class D {}
Even though class D does not implement J, it is still possible for an instance of D to be an instance of J, for example, if the following declaration occurs:
class E extends D implements J {}
For this reason, D is not disjoint from J, and there is a narrowing reference conversion from D to J.
The final clause above implies that two freely extensible interfaces (§9.1.1.4) are not disjoint.
5.1.6.2. Checked and Unchecked Narrowing Reference Conversions
A narrowing reference conversion is either checked or unchecked. These terms refer to the ability of the Java Virtual Machine to validate, or not, the type correctness of the conversion.
If a narrowing reference conversion is unchecked, then the Java Virtual Machine will not be able to fully validate its type correctness, possibly leading to heap pollution (§4.12.2). To flag this to the programmer, an unchecked narrowing reference conversion causes a compile-time unchecked warning, unless suppressed by @SuppressWarnings (§9.6.4.5). Conversely, if a narrowing reference conversion is not unchecked, then it is checked; the Java Virtual Machine will be able to fully validate its type correctness, so no warning is given at compile time.
The unchecked narrowing reference conversions are as follows:
A narrowing reference conversion from a type S to a parameterized class or interface type T is unchecked, unless at least one of the following is true:
All of the type arguments of T are unbounded wildcards.
T <: S, and S has no subtype X other than T where the type arguments of X are not contained in the type arguments of T.
A narrowing reference conversion from a type S to a type variable T is unchecked.
A narrowing reference conversion from a type S to an intersection type T1 & ... & Tn is unchecked if there exists a Ti (1 ≤ i ≤ n) such that S is not a subtype of Ti and a narrowing reference conversion from S to Ti is unchecked.
5.1.6.3. Narrowing Reference Conversions at Run Time
All checked narrowing reference conversions require a validity check at run time. Primarily, these conversions are to class and interface types that are not parameterized.
Some unchecked narrowing reference conversions require a validity check at run time. This depends on whether the unchecked narrowing reference conversion is completely unchecked or partially unchecked. A partially unchecked narrowing reference conversion requires a validity check at run time, while a completely unchecked narrowing reference conversion does not.
These terms refer to the compatibility of the types involved in the conversion when viewed as raw types. If the conversion is conceptually an "upcast", then the conversion is completely unchecked; no run time test is needed because the conversion is legal in the non-generic type system of the Java Virtual Machine. Conversely, if the conversion is conceptually a "downcast", then the conversion is partially unchecked; even in the non-generic type system of the Java Virtual Machine, a run time check is needed to test the compatibility of the (raw) types involved in the conversion.
Using types from the java.util package as an example, a conversion from ArrayList<String> to Collection<T> is completely unchecked, because the (raw) type ArrayList is a subtype of the (raw) type Collection in the Java Virtual Machine. Conversely, a conversion from Collection<T> to ArrayList<String> is partially unchecked, because the (raw) type Collection is not a subtype of the (raw) type ArrayList in the Java Virtual Machine.
The categorization of an unchecked narrowing reference conversion is as follows:
An unchecked narrowing reference conversion from S to a non-intersection type T is completely unchecked if |S| <: |T|.
Otherwise, it is partially unchecked.
An unchecked narrowing reference conversion from S to an intersection type T1 & ... & Tn is completely unchecked if, for all i (1 ≤ i ≤ n), either S <: Ti or a narrowing reference conversion from S to Ti is completely unchecked.
Otherwise, it is partially unchecked.
The run time validity check for a checked or partially unchecked narrowing reference conversion is as follows:
If the value at run time is null, then the conversion is allowed.
Otherwise, let R be the class of the object referred to by the value, and let T be the erasure (§4.6) of the type being converted to. Then:
If R is an ordinary class (not an array class):
If T is a class type, then R must be either the same class as T (§4.3.4) or a subclass of T, or a ClassCastException is thrown.
If T is an interface type, then R must implement interface T (§8.1.5), or a ClassCastException is thrown.
If T is an array type, then a ClassCastException is thrown.
If R is an interface:
Note that R cannot be an interface when these rules are first applied for any given conversion, but R may be an interface if the rules are applied recursively because the run-time reference value may refer to an array whose element type is an interface type.
If T is a class type, then T must be Object (§4.3.2), or a ClassCastException is thrown.
If T is an interface type, then R must be either the same interface as T or a subinterface of T, or a ClassCastException is thrown.
If T is an array type, then a ClassCastException is thrown.
If R is a class representing an array type RC[], that is, an array of components of type RC:
If T is a class type, then T must be Object (§4.3.2), or a ClassCastException is thrown.
If T is an interface type, then T must be the type java.io.Serializable or Cloneable (the only interfaces implemented by arrays), or a ClassCastException is thrown.
If T is an array type TC[], that is, an array of components of type TC, then a ClassCastException is thrown unless either TC and RC are the same primitive type, or TC and RC are reference types and are allowed by a recursive application of these run-time rules.
If the conversion is to an intersection type T1 & ... & Tn, then for all i (1 ≤ i ≤ n), any run-time check required for a conversion from S to Ti is also required for the conversion to the intersection type.
5.1.7. Boxing Conversion
Boxing conversion treats expressions of a primitive type as expressions of a corresponding reference type. Specifically, the following nine conversions are called the boxing conversions:
From type boolean to type Boolean
From type byte to type Byte
From type short to type Short
From type char to type Character
From type int to type Integer
From type long to type Long
From type float to type Float
From type double to type Double
From the null type to the null type
This rule is necessary because the conditional operator (§15.25) applies boxing conversion to the types of its operands, and uses the result in further calculations.
At run time, boxing conversion proceeds as follows:
If p is a value of type boolean, then boxing conversion converts p into a reference r of class and type Boolean, such that r.booleanValue() == p
If p is a value of type byte, then boxing conversion converts p into a reference r of class and type Byte, such that r.byteValue() == p
If p is a value of type char, then boxing conversion converts p into a reference r of class and type Character, such that r.charValue() == p
If p is a value of type short, then boxing conversion converts p into a reference r of class and type Short, such that r.shortValue() == p
If p is a value of type int, then boxing conversion converts p into a reference r of class and type Integer, such that r.intValue() == p
If p is a value of type long, then boxing conversion converts p into a reference r of class and type Long, such that r.longValue() == p
If p is a value of type float then:
If p is not NaN, then boxing conversion converts p into a reference r of class and type Float, such that r.floatValue() evaluates to p
Otherwise, boxing conversion converts p into a reference r of class and type Float such that r.isNaN() evaluates to true
If p is a value of type double, then:
If p is not NaN, boxing conversion converts p into a reference r of class and type Double, such that r.doubleValue() evaluates to p
Otherwise, boxing conversion converts p into a reference r of class and type Double such that r.isNaN() evaluates to true
If p is a value of any other type, boxing conversion is equivalent to an identity conversion (§5.1.1).
If the value p being boxed is the result of evaluating a constant expression (§15.29) of type boolean, byte, char, short, int, or long, and the result is true, false, a character in the range '\u0000' to '\u007f' inclusive, or an integer in the range -128 to 127 inclusive, then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.
Ideally, boxing a primitive value would always yield an identical reference. In practice, this may not be feasible using existing implementation techniques. The rule above is a pragmatic compromise, requiring that certain common values always be boxed into indistinguishable objects. The implementation may cache these, lazily or eagerly. For other values, the rule disallows any assumptions about the identity of the boxed values on the programmer's part. This allows (but does not require) sharing of some or all of these references.
This ensures that in most common cases, the behavior will be the desired one, without imposing an undue performance penalty, especially on small devices. Less memory-limited implementations might, for example, cache all char and short values, as well as int and long values in the range of -32K to +32K.
A boxing conversion may result in an OutOfMemoryError if a new instance of one of the wrapper classes (Boolean, Byte, Character, Short, Integer, Long, Float, or Double) needs to be allocated and insufficient storage is available.
5.1.8. Unboxing Conversion
Unboxing conversion treats expressions of a reference type as expressions of a corresponding primitive type. Specifically, the following eight conversions are called the unboxing conversions:
From type Boolean to type boolean
From type Byte to type byte
From type Short to type short
From type Character to type char
From type Integer to type int
From type Long to type long
From type Float to type float
From type Double to type double
At run time, unboxing conversion proceeds as follows:
If r is a reference of type Boolean, then unboxing conversion converts r into r.booleanValue()
If r is a reference of type Byte, then unboxing conversion converts r into r.byteValue()
If r is a reference of type Character, then unboxing conversion converts r into r.charValue()
If r is a reference of type Short, then unboxing conversion converts r into r.shortValue()
If r is a reference of type Integer, then unboxing conversion converts r into r.intValue()
If r is a reference of type Long, then unboxing conversion converts r into r.longValue()
If r is a reference of type Float, unboxing conversion converts r into r.floatValue()
If r is a reference of type Double, then unboxing conversion converts r into r.doubleValue()
If r is null, unboxing conversion throws a NullPointerException
A type is said to be convertible to a numeric type if it is a numeric type (§4.2), or it is a reference type that may be converted to a numeric type by unboxing conversion.
A type is said to be convertible to an integral type if it is an integral type, or it is a reference type that may be converted to an integral type by unboxing conversion.
5.1.9. Unchecked Conversion
Let G name a generic type declaration with n type parameters.
There is an unchecked conversion from the raw class or interface type (§4.8) G to any parameterized type of the form G<T1,...,Tn>.
There is an unchecked conversion from the raw array type G[]k to any array type of the form G<T1,...,Tn>[]k. (The notation []k indicates an array type of k dimensions.)
Use of an unchecked conversion causes a compile-time unchecked warning unless all type arguments Ti (1 ≤ i ≤ n) are unbounded wildcards (§4.5.1), or the warning is suppressed by @SuppressWarnings (§9.6.4.5).
Unchecked conversion is used to enable a smooth interoperation of legacy code, written before the introduction of generic types, with libraries that have undergone a conversion to use genericity (a process we call generification). In such circumstances (most notably, clients of the Collections Framework in java.util), legacy code uses raw types (e.g. Collection instead of Collection<String>). Expressions of raw types are passed as arguments to library methods that use parameterized versions of those same types as the types of their corresponding formal parameters.
Such calls cannot be shown to be statically safe under the type system using generics. Rejecting such calls would invalidate large bodies of existing code, and prevent them from using newer versions of the libraries. This in turn, would discourage library vendors from taking advantage of genericity. To prevent such an unwelcome turn of events, a raw type may be converted to an arbitrary invocation of the generic type declaration to which the raw type refers. While the conversion is unsound, it is tolerated as a concession to practicality. An unchecked warning is issued in such cases.
5.1.10. Capture Conversion
Let G name a generic type declaration (§8.1.2, §9.1.2) with n type parameters A1,...,An with corresponding bounds U1,...,Un.
There exists a capture conversion from a parameterized type G<T1,...,Tn> (§4.5) to a parameterized type G<S1,...,Sn>, where, for 1 ≤ i ≤ n :
If Ti is a wildcard type argument (§4.5.1) of the form ?, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is the null type (§4.1).
If Ti is a wildcard type argument of the form ? extends Bi, then Si is a fresh type variable whose upper bound is glb(Bi, Ui[A1:=S1,...,An:=Sn]) and whose lower bound is the null type.
glb(V1,...,Vm) is defined as V1 & ... & Vm.
It is a compile-time error if, for any two classes (not interfaces) Vi and Vj, Vi is not a subclass of Vj or vice versa.
If Ti is a wildcard type argument of the form ? super Bi, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is Bi.
Otherwise, Si = Ti.
Capture conversion on any type other than a parameterized type (§4.5) acts as an identity conversion (§5.1.1).
Capture conversion is not applied recursively.
Capture conversion never requires a special action at run time and therefore never throws an exception at run time.
Capture conversion is designed to make wildcards more useful. To understand the motivation, let's begin by looking at the method java.util.Collections.reverse():
public static void reverse(List<?> list);
The method reverses the list provided as a parameter. It works for any type of list, and so the use of the wildcard type List<?> as the type of the formal parameter is entirely appropriate.
Now consider how one would implement reverse():
public static void reverse(List<?> list) { rev(list); }
private static <T> void rev(List<T> list) {
List<T> tmp = new ArrayList<T>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1));
}
}
The implementation needs to copy the list, extract elements from the copy, and insert them into the original. To do this in a type-safe manner, we need to give a name, T, to the element type of the incoming list. We do this in the private service method rev(). This requires us to pass the incoming argument list, of type List<?>, as an argument to rev(). In general, List<?> is a list of unknown type. It is not a subtype of List<T>, for any type T. Allowing such a subtype relation would be unsound. Given the method:
public static <T> void fill(List<T> l, T obj)
the following code would undermine the type system:
List<String> ls = new ArrayList<String>();
List<?> l = ls;
Collections.fill(l, new Object()); // not legal - but assume it was!
String s = ls.get(0); // ClassCastException - ls contains
// Objects, not Strings.
So, without some special dispensation, we can see that the call from reverse() to rev() would be disallowed. If this were the case, the author of reverse() would be forced to write its signature as:
public static <T> void reverse(List<T> list)
This is undesirable, as it exposes implementation information to the caller. Worse, the designer of an API might reason that the signature using a wildcard is what the callers of the API require, and only later realize that a type safe implementation was precluded.
The call from reverse() to rev() is in fact harmless, but it cannot be justified on the basis of a general subtyping relation between List<?> and List<T>. The call is harmless, because the incoming argument is doubtless a list of some type (albeit an unknown one). If we can capture this unknown type in a type variable X, we can infer T to be X. That is the essence of capture conversion. The specification of course must cope with complications, like non-trivial (and possibly recursively defined) upper or lower bounds, the presence of multiple arguments etc.
Mathematically sophisticated readers will want to relate capture conversion to established type theory. Readers unfamiliar with type theory can skip this discussion - or else study a suitable text, such as Types and Programming Languages by Benjamin Pierce, and then revisit this section.
Here then is a brief summary of the relationship of capture conversion to established type theoretical notions. Wildcard types are a restricted form of existential types. Capture conversion corresponds loosely to an opening of a value of existential type. A capture conversion of an expression e can be thought of as an open of e in a scope that comprises the top level expression that encloses e.
The classical open operation on existentials requires that the captured type variable must not escape the opened expression. The open that corresponds to capture conversion is always on a scope sufficiently large that the captured type variable can never be visible outside that scope. The advantage of this scheme is that there is no need for a close operation, as defined in the paper On Variance-Based Subtyping for Parametric Types by Atsushi Igarashi and Mirko Viroli, in the proceedings of the 16th European Conference on Object Oriented Programming (ECOOP 2002). For a formal account of wildcards, see Wild FJ by Mads Torgersen, Erik Ernst and Christian Plesner Hansen, in the 12th workshop on Foundations of Object Oriented Programming (FOOL 2005).
5.1.11. String Conversion
Any type may be converted to type String by string conversion.
A value x of primitive type T is first converted to a reference value as if by giving it as an argument to an appropriate class instance creation expression (§15.9):
If T is boolean, then use new Boolean(x).
If T is char, then use new Character(x).
If T is byte, short, or int, then use new Integer(x).
If T is long, then use new Long(x).
If T is float, then use new Float(x).
If T is double, then use new Double(x).
This reference value is then converted to type String by string conversion.
Now only reference values need to be considered:
If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l).
Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead.
The toString method is defined by the primordial class Object (§4.3.2). Many classes override it, notably Boolean, Character, Integer, Long, Float, Double, and String.
5.1.12. Forbidden Conversions
Any conversion that is not explicitly allowed is forbidden.
5.2. Assignment Contexts
Assignment contexts allow the value of an expression to be assigned (§15.26) to a variable; the type of the expression must be converted to the type of the variable.
Assignment contexts allow the use of one of the following:
an identity conversion (§5.1.1)
a widening primitive conversion (§5.1.2)
a widening reference conversion (§5.1.5)
a widening reference conversion followed by an unboxing conversion
a widening reference conversion followed by an unboxing conversion, then followed by a widening primitive conversion
a boxing conversion (§5.1.7)
a boxing conversion followed by a widening reference conversion
an unboxing conversion (§5.1.8)
an unboxing conversion followed by a widening primitive conversion
If, after the conversions listed above have been applied, the resulting type is a raw type (§4.8), an unchecked conversion (§5.1.9) may then be applied.
In addition, if the expression is a constant expression (§15.29) of type byte, short, char, or int:
A narrowing primitive conversion may be used if the variable is of type byte, short, or char, and the value of the constant expression is representable in the type of the variable.
A narrowing primitive conversion followed by a boxing conversion may be used if the variable is of type Byte, Short, or Character, and the value of the constant expression is representable in the type byte, short, or char respectively.
The compile-time narrowing of constant expressions means that code such as:
byte theAnswer = 42;
is allowed. Without the narrowing, the fact that the integer literal 42 has type int would mean that a cast to byte would be required:
byte theAnswer = (byte)42; // cast is permitted but not required
Finally, a value of the null type (the null reference is the only such value) may be assigned to any reference type, resulting in a null reference of that type.
It is a compile-time error if the chain of conversions contains two parameterized types that are not in the subtype relation (§4.10).
An example of such an illegal chain would be:
Integer, Comparable<Integer>, Comparable, Comparable<String>
The first three elements of the chain are related by widening reference conversion, while the last entry is derived from its predecessor by unchecked conversion. However, this is not a valid assignment conversion, because the chain contains two parameterized types, Comparable<Integer> and Comparable<String>, that are not subtypes.
If the type of an expression can be converted to the type of a variable by assignment conversion, we say the expression (or its value) is assignable to the variable or, equivalently, that the type of the expression is assignment compatible with the type of the variable.
The only exceptions that may arise from conversions in an assignment context are:
A ClassCastException if, after the conversions above have been applied, the resulting value is an object which is not an instance of a subclass or subinterface of the erasure (§4.6) of the type of the variable.
This circumstance can only arise as a result of heap pollution (§4.12.2). In practice, implementations need only perform casts when accessing a field or method of an object of parameterized type when the erased type of the field, or the erased return type of the method, differ from its unerased type.
An OutOfMemoryError as a result of a boxing conversion.
A NullPointerException as a result of an unboxing conversion on a null reference.
An ArrayStoreException in special cases involving array elements or field access (§10.5, §15.26.1).
Example 5.2-1. Assignment for Primitive Types
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);
}
}
This program produces the output:
f=12.0
l=0x123
d=1.2300000190734863
The following program, however, produces compile-time errors:
class Test {
public static void main(String[] args) {
short s = 123;
char c = s; // error: would require cast
s = c; // error: would require cast
}
}
because not all short values are char values, and neither are all char values short values.
Example 5.2-2. Assignment for Reference Types
class Point { int x, y; }
class Point3D extends Point { int z; }
interface Colorable { void setColor(int color); }
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
}
}
The following test program illustrates assignment conversions on reference values, but fails to compile, as described in its comments. This example should be compared to the preceding one.
class Point { int x, y; }
interface Colorable { void setColor(int color); }
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
}
}
Example 5.2-3. Assignment for Array Types
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
}
}
In this example:
The value of veclong cannot be assigned to a Long variable, because Long is a class type other than Object. An array can be assigned only to a variable of a compatible array type, or to a variable of type Object, Cloneable or java.io.Serializable.
The value of veclong cannot be assigned to vecshort, because they are arrays of primitive type, and short and long are not the same primitive type.
The value of cpvec can be assigned to pvec, because any reference that could be the value of an expression of type ColoredPoint can be the value of a variable of type Point. The subsequent assignment of the new Point to a component of pvec then would throw an ArrayStoreException (if the program were otherwise corrected so that it could be compiled), because a ColoredPoint array cannot have an instance of Point as the value of a component.
The value of pvec cannot be assigned to cpvec, because not every reference that could be the value of an expression of type Point can correctly be the value of a variable of type ColoredPoint. If the value of pvec at run time were a reference to an instance of Point[], and the assignment to cpvec were allowed, a simple reference to a component of cpvec, say, cpvec[0], could return a Point, and a Point is not a ColoredPoint. Thus to allow such an assignment would allow a violation of the type system. A cast may be used (§5.5, §15.16) to ensure that pvec references a ColoredPoint[]:
cpvec = (ColoredPoint[])pvec; // OK, but may throw an
// exception at run time
5.3. Invocation Contexts
Invocation contexts allow an argument value in a method or constructor invocation (§8.8.7.1, §15.9, §15.12) to be assigned to a corresponding formal parameter.
Strict invocation contexts allow the use of one of the following:
an identity conversion (§5.1.1)
a widening primitive conversion (§5.1.2)
a widening reference conversion (§5.1.5)
Loose invocation contexts allow a more permissive set of conversions, because they are only used for a particular invocation if no applicable declaration can be found using strict invocation contexts. Loose invocation contexts allow the use of one of the following:
an identity conversion (§5.1.1)
a widening primitive conversion (§5.1.2)
a widening reference conversion (§5.1.5)
a widening reference conversion followed by an unboxing conversion
a widening reference conversion followed by an unboxing conversion, then followed by a widening primitive conversion
a boxing conversion (§5.1.7)
a boxing conversion followed by widening reference conversion
an unboxing conversion (§5.1.8)
an unboxing conversion followed by a widening primitive conversion
If, after the conversions listed for an invocation context have been applied, the resulting type is a raw type (§4.8), an unchecked conversion (§5.1.9) may then be applied.
A value of the null type (the null reference is the only such value) may be assigned to any reference type.
It is a compile-time error if the chain of conversions contains two parameterized types that are not in the subtype relation (§4.10).
The only exceptions that may arise in an invocation context are:
A ClassCastException if, after the type conversions above have been applied, the resulting value is an object which is not an instance of a subclass or subinterface of the erasure (§4.6) of the corresponding formal parameter type.
An OutOfMemoryError as a result of a boxing conversion.
A NullPointerException as a result of an unboxing conversion on a null reference.
Neither strict nor loose invocation contexts include the implicit narrowing of integer constant expressions which is allowed in assignment contexts. The designers of the Java programming language felt that including these implicit narrowing conversions would add additional complexity to the rules of overload resolution (§15.12.2).
Thus, the program:
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
}
}