Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4d47d0a

Browse files
author
siyuanzhou
committed
更新翻译 第20章到问题章节
Signed-off-by: siyuanzhou <siyuanzhou@163..com>
1 parent 1ed6c5f commit 4d47d0a

2 files changed

Lines changed: 284 additions & 14 deletions

File tree

‎docs/book/20-Generics.md‎

Lines changed: 255 additions & 13 deletions
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‎

Lines changed: 29 additions & 1 deletion
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)

AltStyle によって変換されたページ (->オリジナル) /