Skip to content

Commit 4d47d0a

Browse files
author
siyuanzhou
committed
更新翻译 第20章到问题章节
Signed-off-by: siyuanzhou <[email protected]>
1 parent 1ed6c5f commit 4d47d0a

File tree

2 files changed

+284
-14
lines changed

2 files changed

+284
-14
lines changed

docs/book/20-Generics.md

+255-13
Original file line numberDiff line numberDiff line change
@@ -2832,9 +2832,6 @@ public class GenericReading {
28322832

28332833
```java
28342834
// 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.
28382835
import java.util.*;
28392836

28402837
public class UnboundedWildcards1 {
@@ -2896,9 +2893,6 @@ public class UnboundedWildcards1 {
28962893

28972894
```java
28982895
// 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.
29022896
import java.util.*;
29032897

29042898
public class UnboundedWildcards2 {
@@ -2939,9 +2933,6 @@ public class UnboundedWildcards2 {
29392933

29402934
```java
29412935
// 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.
29452936
// Exploring the meaning of wildcards
29462937

29472938
public class Wildcards {
@@ -3244,9 +3235,6 @@ public class Wildcards {
32443235

32453236
```java
32463237
// 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.
32503238

32513239
public class CaptureConversion {
32523240
static <T> void f1(Holder<T> holder) {
@@ -3312,6 +3300,255 @@ Double
33123300

33133301
## 问题
33143302

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+
33153552
<!-- Self-Bounded Types -->
33163553

33173554
## 自限定的类型
@@ -3325,7 +3562,12 @@ class SelfBounded<T extends SelfBounded<T>> { // ...
33253562
```
33263563

33273564
这就像两面镜子彼此照向对方所引起的目眩效果一样,是一种无限反射。**SelfBounded** 类接受泛型参数 **T**,而T由一个边界类限定,这个边界就是拥有 **T** 作为其参数的 **SelfBounded**
3328-
当你首次看到它时,很难去解析它,它强调的是当 `extends` 关键字用于边界与用来创建子类明显是不同的。
3565+
当你首次看到它时,很难去解析它,它强调的是当 **extends** 关键字用于边界与用来创建子类明显是不同的。
3566+
3567+
### 古怪的循环泛型
3568+
3569+
为了理解自限定类型的含义,我们从这个惯用法的一个简单版本入手,它没有自限定的边界。
3570+
不能直接继承一个泛型参数,但是,可以继承在其自己的定义中使用这个泛型参数的类。也就是说,可以声明:
33293571

33303572
## 动态类型安全
33313573

docs/book/Appendix-Passing-and-Returning-Objects.md

+29-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,40 @@
33
<!-- Appendix: Passing and Returning Objects -->
44
# 附录:对象传递和返回
55

6+
> 到现在为止,你已经对“传递”对象实际上是传递引用这一想法想法感到满意。
7+
8+
在许多编程语言中,你可以使用该语言的“常规”方式来传递对象,并且大多数情况下一切正常。 但是通常会出现这种情况,你必须做一些不平常的事情,突然事情变得更加复杂。 Java也不例外,当您传递对象并对其进行操作时,准确了解正在发生的事情很重要。 本附录提供了这种见解。
9+
10+
提出本附录问题的另一种方法是,如果你之前使用类似C++的编程语言,则是“ Java是否有指针?” Java中的每个对象标识符(除原语外)都是这些指针之一,但它们的用法是不仅受编译器的约束,而且受运行时系统的约束。 换一种说法,Java有指针,但没有指针算法。 这些就是我一直所说的“引用”,您可以将它们视为“安全指针”,与小学的安全剪刀不同-它们不敏锐,因此您不费吹灰之力就无法伤害自己,但是它们有时可能很乏味。
611

712
<!-- Passing References -->
8-
## 传递引用
913

14+
## 传递引用
1015

1116
<!-- Making Local Copies -->
17+
18+
当你将引用传递给方法时,它仍指向同一对象。 一个简单的实验演示了这一点:
19+
20+
```java
21+
// references/PassReferences.java
22+
public class PassReferences {
23+
public static void f(PassReferences h) {
24+
System.out.println("h inside f(): " + h);
25+
}
26+
public static void main(String[] args) {
27+
PassReferences p = new PassReferences();
28+
System.out.println("p inside main(): " + p);
29+
f(p);
30+
}
31+
}
32+
/* Output:
33+
p inside main(): PassReferences@15db9742
34+
h inside f(): PassReferences@15db9742
35+
*/
36+
```
37+
38+
方法 `toString() ` 在打印语句中自动调用,并且 `PassReferences` 直接从 `Object` 继承而无需重新定义 `toString()` 。 因此,使用的是 `Object``toString()` 版本,它打印出对象的类,然后打印出该对象所在的地址(不是引用,而是实际的对象存储)。
39+
1240
## 本地拷贝
1341

1442

0 commit comments

Comments
 (0)