@@ -2832,9 +2832,6 @@ public class GenericReading {
2832
2832
2833
2833
```java
2834
2834
// generics/UnboundedWildcards1.java
2835
- // (c)2017 MindView LLC: see Copyright.txt
2836
- // We make no guarantees that this code is fit for any purpose.
2837
- // Visit http://OnJava8.com for more book information.
2838
2835
import java.util.*;
2839
2836
2840
2837
public class UnboundedWildcards1 {
@@ -2896,9 +2893,6 @@ public class UnboundedWildcards1 {
2896
2893
2897
2894
```java
2898
2895
// generics/UnboundedWildcards2.java
2899
- // (c)2017 MindView LLC: see Copyright.txt
2900
- // We make no guarantees that this code is fit for any purpose.
2901
- // Visit http://OnJava8.com for more book information.
2902
2896
import java. util. * ;
2903
2897
2904
2898
public class UnboundedWildcards2 {
@@ -2939,9 +2933,6 @@ public class UnboundedWildcards2 {
2939
2933
2940
2934
```java
2941
2935
// generics/Wildcards.java
2942
- // (c)2017 MindView LLC: see Copyright.txt
2943
- // We make no guarantees that this code is fit for any purpose.
2944
- // Visit http://OnJava8.com for more book information.
2945
2936
// Exploring the meaning of wildcards
2946
2937
2947
2938
public class Wildcards {
@@ -3244,9 +3235,6 @@ public class Wildcards {
3244
3235
3245
3236
```java
3246
3237
// generics/CaptureConversion.java
3247
- // (c)2017 MindView LLC: see Copyright.txt
3248
- // We make no guarantees that this code is fit for any purpose.
3249
- // Visit http://OnJava8.com for more book information.
3250
3238
3251
3239
public class CaptureConversion {
3252
3240
static <T > void f1 (Holder<T > holder ) {
@@ -3312,6 +3300,255 @@ Double
3312
3300
3313
3301
## 问题
3314
3302
3303
+ 本节将阐述在使用Java 泛型时会出现的各类问题。
3304
+
3305
+ ### 任何基本类型都不能作为类型参数
3306
+
3307
+ 正如本章早先提到过的,你将在Java 泛型中发现的限制之一是,不能将基本类型用作类型参数。因此,不能创建 `ArrayList<int> ` 之类的东西。
3308
+ 解决之道是使用基本类型的包装器类以及JavaSE5 的自动包装机制。如果创建一个 `ArrayList<Integer > `,并将基本类型 ** int ** 应用于这个容器,那么你将发现自动包装机制将自动地实现 ** int ** 到 ** Integer ** 的双向转换——因此,这几乎就像是有一个 `ArrayList<int> `一样:
3309
+
3310
+ ```java
3311
+ // generics/ListOfInt.java
3312
+ // Autoboxing compensates for the inability
3313
+ // to use primitives in generics
3314
+ import java.util.*;
3315
+ import java. util. stream. * ;
3316
+
3317
+ public class ListOfInt {
3318
+ public static void main (String [] args ) {
3319
+ List<Integer > li = IntStream . range(38 , 48 )
3320
+ .boxed() // Converts ints to Integers
3321
+ .collect(Collectors . toList());
3322
+ System . out. println(li);
3323
+ }
3324
+ }
3325
+ /* Output:
3326
+ [38, 39, 40, 41, 42, 43, 44, 45, 46, 47]
3327
+ */
3328
+ ```
3329
+
3330
+ 注意,自动包装机制甚至允许用foreach语法来产生 ** int ** 。
3331
+ 通常,这种解决方案工作得很好——能够成功地存储和读取 ** int ** ,有一些转换碰巧在发生的同时会对你屏蔽掉。但是,如果性能成为了问题,就需要使用专门适配基本类型的容器版。** Org . apache. commons. collections. primitives** 就是一种开源的这类版本。
3332
+ 下面是另外一种方式,它可以创建持有 ** Byte ** 的 ** Set ** :
3333
+
3334
+ ```java
3335
+ // generics/ByteSet.java
3336
+ import java. util. * ;
3337
+
3338
+ public class ByteSet {
3339
+ Byte [] possibles = { 1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 };
3340
+ Set<Byte > mySet =
3341
+ new HashSet<> (Arrays . asList(possibles));
3342
+ // But you can't do this:
3343
+ // Set<Byte> mySet2 = new HashSet<>(
3344
+ // Arrays.<Byte>asList(1,2,3,4,5,6,7,8,9));
3345
+ }
3346
+ ```
3347
+
3348
+ 注意,自动包装机制解决了一些问题,但并不是解决了所有问题。
3349
+
3350
+ 在下面的示例中,** FillArray** 接口包含一些通用方法,这些方法使用 **Supplier** 来用对象填充数组(这使得类泛型在本例中无法工作,因为这个方法是静态的) **Supplier** 实现来自“数组”一章,并且在 `main ()` 中,可以看到 `FillArray.fill ()` 使用它在数组中填充对象:
3351
+
3352
+ ```java
3353
+ // generics/PrimitiveGenericTest.java
3354
+ import onjava.*;
3355
+ import java. util. * ;
3356
+ import java. util. function. * ;
3357
+
3358
+ // Fill an array using a generator:
3359
+ interface FillArray {
3360
+ static <T > T [] fill (T [] a , Supplier<T > gen ) {
3361
+ Arrays . setAll(a, n - > gen. get());
3362
+ return a;
3363
+ }
3364
+ static int [] fill (int [] a , IntSupplier gen ) {
3365
+ Arrays . setAll(a, n - > gen. getAsInt());
3366
+ return a;
3367
+ }
3368
+ static long [] fill (long [] a , LongSupplier gen ) {
3369
+ Arrays . setAll(a, n - > gen. getAsLong());
3370
+ return a;
3371
+ }
3372
+ static double [] fill (double [] a , DoubleSupplier gen ) {
3373
+ Arrays . setAll(a, n - > gen. getAsDouble());
3374
+ return a;
3375
+ }
3376
+ }
3377
+
3378
+ public class PrimitiveGenericTest {
3379
+ public static void main (String [] args ) {
3380
+ String [] strings = FillArray . fill(
3381
+ new String [5 ], new Rand .String (9 ));
3382
+ System . out. println(Arrays . toString(strings));
3383
+ int [] integers = FillArray . fill(
3384
+ new int [9 ], new Rand .Pint ());
3385
+ System . out. println(Arrays . toString(integers));
3386
+ }
3387
+ }
3388
+ /* Output:
3389
+ [btpenpccu, xszgvgmei, nneeloztd, vewcippcy, gpoalkljl]
3390
+ [635, 8737, 3941, 4720, 6177, 8479, 6656, 3768, 4948]
3391
+ */
3392
+ ```
3393
+
3394
+ 自动装箱不适用于数组,因此我们必须创建 `FillArray.fill ()` 的重载版本,或创建产生 **Wrapped** 输出的生成器。 **FillArray** 仅比 `java.util.Arrays.setAll ()` 有用,因为它返回填充的数组。
3395
+
3396
+ ### 实现参数化接口
3397
+
3398
+ 一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。下面是产生这种冲突的情况:
3399
+
3400
+ ```java
3401
+ // generics/MultipleInterfaceVariants.java
3402
+ // {WillNotCompile}
3403
+ package generics;
3404
+
3405
+ interface Payable <T> {}
3406
+
3407
+ class Employee implements Payable<Employee > {}
3408
+
3409
+ class Hourly extends Employee
3410
+ implements Payable<Hourly > {}
3411
+ ```
3412
+
3413
+ ** Hourly ** 不能编译,因为擦除会将 `Payable<Employe > ` 和 `Payable<Hourly > ` 简化为相同的类 ** Payable ** ,这样,上面的代码就意味着在重复两次地实现相同的接口。十分有趣的是,如果从 ** Payable ** 的两种用法中都移除掉泛型参数(就像编译器在擦除阶段所做的那样)这段代码就可以编译。
3414
+
3415
+ 在使用某些更基本的 Java 接口,例如 `Comparable<T > ` 时,这个问题可能会变得十分令人恼火,就像你在本节稍后就会看到的那样。
3416
+
3417
+ ### 转型和警告
3418
+
3419
+ 使用带有泛型类型参数的转型或 ** instanceof ** 不会有任何效果。下面的容器在内部将各个值存储为 ** Object ** ,并在获取这些值时,再将它们转型回 ** T ** :
3420
+
3421
+ ```java
3422
+ // generics/GenericCast.java
3423
+ import java. util. * ;
3424
+ import java. util. stream. * ;
3425
+
3426
+ class FixedSizeStack <T> {
3427
+ private final int size;
3428
+ private Object [] storage;
3429
+ private int index = 0 ;
3430
+ FixedSizeStack (int size ) {
3431
+ this . size = size;
3432
+ storage = new Object [size];
3433
+ }
3434
+ public void push (T item ) {
3435
+ if (index < size)
3436
+ storage[index++ ] = item;
3437
+ }
3438
+ @SuppressWarnings (" unchecked" )
3439
+ public T pop () {
3440
+ return index == 0 ? null : (T )storage[-- index];
3441
+ }
3442
+ @SuppressWarnings (" unchecked" )
3443
+ Stream<T > stream () {
3444
+ return (Stream<T > )Arrays . stream(storage);
3445
+ }
3446
+ }
3447
+
3448
+ public class GenericCast {
3449
+ static String [] letters =
3450
+ " ABCDEFGHIJKLMNOPQRS" . split(" " );
3451
+ public static void main (String [] args ) {
3452
+ FixedSizeStack<String > strings =
3453
+ new FixedSizeStack<> (letters. length);
3454
+ Arrays . stream(" ABCDEFGHIJKLMNOPQRS" . split(" " ))
3455
+ .forEach(strings:: push);
3456
+ System . out. println(strings. pop());
3457
+ strings. stream()
3458
+ .map(s - > s + " " )
3459
+ .forEach(System . out:: print);
3460
+ }
3461
+ }
3462
+ /* Output:
3463
+ S
3464
+ A B C D E F G H I J K L M N O P Q R S
3465
+ */
3466
+ ```
3467
+
3468
+ 如果没有 ** @SuppressWarnings ** 注解,编译器将对 `pop ()` 产生 “unchecked cast” 警告。由于擦除的原因,编译器无法知道这个转型是否是安全的,并且 `pop ()` 方法实际上并没有执行任何转型。
3469
+ 这是因为,**T** 被擦除到它的第一个边界,默认情况下是 **Object** ,因此 `pop ()` 实际上只是将 **Object** 转型为 **Object**。
3470
+ 有时,泛型没有消除对转型的需要,这就会由编译器产生警告,而这个警告是不恰当的。例如:
3471
+
3472
+ ```java
3473
+ // generics/NeedCasting.java
3474
+ import java.io.*;
3475
+ import java. util. * ;
3476
+
3477
+ public class NeedCasting {
3478
+ @SuppressWarnings (" unchecked" )
3479
+ public void f (String [] args ) throws Exception {
3480
+ ObjectInputStream in = new ObjectInputStream (
3481
+ new FileInputStream (args[0 ]));
3482
+ List<Widget > shapes = (List<Widget > )in. readObject();
3483
+ }
3484
+ }
3485
+ ```
3486
+
3487
+ 正如你将在附件:对象序列化( Appendix : Object Serialization )中学到的那样,`readObject () `无法知道它正在读取的是什么,因此它返回的是必须转型的对象。但是当注释掉 **@SuppressWarnings** 注解,并编译这个程序时,就会得到下面的警告。
3488
+
3489
+ ```
3490
+ NeedCasting.java uses unchecked or unsafe operations.
3491
+ Recompile with -Xlint:unchecked for details.
3492
+
3493
+ And if you follow the instructions and recompile with -
3494
+ Xlint:unchecked :(如果遵循这条指示,使用-Xlint:unchecked来重新编译:)
3495
+
3496
+ NeedCasting.java:10: warning: [unchecked] unchecked cast
3497
+ List<Widget > shapes = (List<Widget > )in.readObject ();
3498
+ required: List<Widget >
3499
+ found: Object
3500
+ 1 warning
3501
+ ```
3502
+
3503
+ 你会被强制要求转型,但是又被告知不应该转型。为了解决这个问题,必须使用在 Java SE5 中引入的新的转型形式,既通过泛型类来转型:
3504
+
3505
+ ```java
3506
+ // generics/ClassCasting.java
3507
+ import java. io. * ;
3508
+ import java. util. * ;
3509
+
3510
+ public class ClassCasting {
3511
+ @SuppressWarnings (" unchecked" )
3512
+ public void f (String [] args ) throws Exception {
3513
+ ObjectInputStream in = new ObjectInputStream (
3514
+ new FileInputStream (args[0 ]));
3515
+ // Won't Compile:
3516
+ // List<Widget> lw1 =
3517
+ // List<>.class.cast(in.readObject());
3518
+ List<Widget > lw2 = List . class. cast(in. readObject());
3519
+ }
3520
+ }
3521
+ ```
3522
+
3523
+ 但是,不能转型到实际类型( `List<Widget > ` )。也就是说,不能声明:
3524
+
3525
+ ```
3526
+ List<Widget > .class.cast (in .readobject ())
3527
+ ```
3528
+
3529
+ 甚至当你添加一个像下面这样的另一个转型时:
3530
+
3531
+ ```
3532
+ (List<Widget > )List.class.cast (in .readobject ())
3533
+ ```
3534
+
3535
+ 仍旧会得到一个警告。
3536
+
3537
+ ### 重载
3538
+
3539
+ 下面的程序是不能编译的,即使编译它是一种合理的尝试:
3540
+
3541
+ ```java
3542
+ // generics/UseList.java
3543
+ // {WillNotCompile}
3544
+ import java.util.*;
3545
+
3546
+ public class UseList <W, T> {
3547
+ void f (List<T > v ) {}
3548
+ void f (List<W > v ) {}
3549
+ }
3550
+ ```
3551
+
3315
3552
< ! -- Self - Bounded Types -- >
3316
3553
3317
3554
## 自限定的类型
@@ -3325,7 +3562,12 @@ class SelfBounded<T extends SelfBounded<T>> { // ...
3325
3562
```
3326
3563
3327
3564
这就像两面镜子彼此照向对方所引起的目眩效果一样,是一种无限反射。** SelfBounded ** 类接受泛型参数 ** T ** ,而T 由一个边界类限定,这个边界就是拥有 ** T ** 作为其参数的 ** SelfBounded ** 。
3328
- 当你首次看到它时,很难去解析它,它强调的是当 `extends` 关键字用于边界与用来创建子类明显是不同的。
3565
+ 当你首次看到它时,很难去解析它,它强调的是当 ** extends** 关键字用于边界与用来创建子类明显是不同的。
3566
+
3567
+ ### 古怪的循环泛型
3568
+
3569
+ 为了理解自限定类型的含义,我们从这个惯用法的一个简单版本入手,它没有自限定的边界。
3570
+ 不能直接继承一个泛型参数,但是,可以继承在其自己的定义中使用这个泛型参数的类。也就是说,可以声明:
3329
3571
3330
3572
## 动态类型安全
3331
3573
0 commit comments