Skip to content

Commit b71e7bb

Browse files
committed
泛型 - 泛型接口
泛型接口小节翻译完成
1 parent 41aa629 commit b71e7bb

File tree

1 file changed

+205
-1
lines changed

1 file changed

+205
-1
lines changed

docs/book/20-Generics.md

Lines changed: 205 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -359,11 +359,215 @@ brown over fox quick quick dog brown The brown lazy brown
359359
`RandomList` 继承了 `ArrayList` 的所有方法。本例中只添加了 `select()` 这个方法。
360360

361361
<!-- Generic Interfaces -->
362-
363362
## 泛型接口
364363

364+
泛型也可以应用于接口。例如 *生成器*,这是一种专门负责创建对象的类。实际上,这是 *工厂方法* 设计模式的一种应用。不过,当使用生成器创建新的对象时,它不需要任何参数,而工厂方法一般需要参数。生成器无需额外的信息就知道如何创建新对象。
365+
366+
一般而言,一个生成器只定义一个方法,用于创建对象。例如 `java.util.function` 类库中的 `Supplier` 就是一个生成器,调用其 `get()` 获取对象。`get()` 是泛型方法,返回值为类型参数 `T`
367+
368+
为了演示 `Supplier`,我们需要定义几个类。下面是个咖啡相关的继承体系:
369+
370+
```java
371+
// generics/coffee/Coffee.java
372+
package generics.coffee;
373+
374+
public class Coffee {
375+
private static long counter = 0;
376+
private final long id = counter++;
377+
378+
@Override
379+
public String toString() {
380+
return getClass().getSimpleName() + " " + id;
381+
}
382+
}
383+
384+
385+
// generics/coffee/Latte.java
386+
package generics.coffee;
387+
public class Latte extends Coffee {}
388+
389+
390+
// generics/coffee/Mocha.java
391+
package generics.coffee;
392+
public class Mocha extends Coffee {}
393+
394+
395+
// generics/coffee/Cappuccino.java
396+
package generics.coffee;
397+
public class Cappuccino extends Coffee {}
398+
399+
400+
// generics/coffee/Americano.java
401+
package generics.coffee;
402+
public class Americano extends Coffee {}
403+
404+
405+
// generics/coffee/Breve.java
406+
package generics.coffee;
407+
public class Breve extends Coffee {}
408+
```
409+
410+
现在,我们可以编写一个类,实现 `Supplier<Coffee>` 接口,它能够随机生成不同类型的 `Coffee` 对象:
411+
412+
```java
413+
// generics/coffee/CoffeeSupplier.java
414+
// {java generics.coffee.CoffeeSupplier}
415+
package generics.coffee;
416+
import java.util.*;
417+
import java.util.function.*;
418+
import java.util.stream.*;
419+
420+
public class CoffeeSupplier
421+
implements Supplier<Coffee>, Iterable<Coffee> {
422+
private Class<?>[] types = { Latte.class, Mocha.class,
423+
Cappuccino.class, Americano.class, Breve.class };
424+
private static Random rand = new Random(47);
425+
426+
public CoffeeSupplier() {}
427+
// For iteration:
428+
private int size = 0;
429+
public CoffeeSupplier(int sz) { size = sz; }
430+
431+
@Override
432+
public Coffee get() {
433+
try {
434+
return (Coffee) types[rand.nextInt(types.length)].newInstance();
435+
} catch (InstantiationException | IllegalAccessException e) {
436+
throw new RuntimeException(e);
437+
}
438+
}
439+
440+
class CoffeeIterator implements Iterator<Coffee> {
441+
int count = size;
442+
@Override
443+
public boolean hasNext() { return count > 0; }
444+
@Override
445+
public Coffee next() {
446+
count--;
447+
return CoffeeSupplier.this.get();
448+
}
449+
@Override
450+
public void remove() {
451+
throw new UnsupportedOperationException();
452+
}
453+
}
454+
455+
@Override
456+
public Iterator<Coffee> iterator() {
457+
return new CoffeeIterator();
458+
}
459+
460+
public static void main(String[] args) {
461+
Stream.generate(new CoffeeSupplier())
462+
.limit(5)
463+
.forEach(System.out::println);
464+
for (Coffee c : new CoffeeSupplier(5)) {
465+
System.out.println(c);
466+
}
467+
}
468+
}
469+
```
470+
471+
输出结果:
472+
473+
```java
474+
Americano 0
475+
Latte 1
476+
Americano 2
477+
Mocha 3
478+
Mocha 4
479+
Breve 5
480+
Americano 6
481+
Latte 7
482+
Cappuccino 8
483+
Cappuccino 9
484+
```
485+
486+
参数化的 `Supplier` 接口确保 `get()` 返回值是参数的类型。`CoffeeSupplier` 同时还实现了 `Iterable` 接口,所以能用于 *for-in* 语句。不过,它还需要知道何时终止循环,这正是第二个构造函数的作用。
487+
488+
下面是另一个实现 `Supplier<T>` 接口的例子,它负责生成 Fibonacci 数列:
489+
490+
```java
491+
// generics/Fibonacci.java
492+
// Generate a Fibonacci sequence
493+
import java.util.function.*;
494+
import java.util.stream.*;
495+
496+
public class Fibonacci implements Supplier<Integer> {
497+
private int count = 0;
498+
@Override
499+
public Integer get() { return fib(count++); }
500+
501+
private int fib(int n) {
502+
if(n < 2) return 1;
503+
return fib(n-2) + fib(n-1);
504+
}
505+
506+
public static void main(String[] args) {
507+
Stream.generate(new Fibonacci())
508+
.limit(18)
509+
.map(n -> n + " ")
510+
.forEach(System.out::print);
511+
}
512+
}
513+
```
514+
515+
输出结果:
516+
517+
```java
518+
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
519+
```
520+
521+
虽然我们在 `Fibonacci` 类的里里外外使用的都是 `int` 类型,但是其参数类型却是 `Integer`。这个例子引出了 Java 泛型的一个局限性:基本类型无法作为类型参数。不过 Java 5 具备自动装箱和拆箱的功能,可以很方便地在基本类型和相应的包装类之间进行转换。通过这个例子中 `Fibonacci` 类对 `int` 的使用,我们已经看到了这种效果。
522+
523+
如果还想更进一步,编写一个实现了 `Iterator``Fibnoacci` 生成器。我们的一个选择是重写这个类,令其实现 `Iterator` 接口。不过,你并不是总能拥有源代码的控制权,并且,除非必须这么做,否则,我们也不愿意重写一个类。而且我们还有另一种选择,就是创建一个 *适配器* (Adapter) 来实现所需的接口,我们在前面介绍过这个设计模式。
524+
525+
有多种方法可以实现适配器。例如,可以通过继承来创建适配器类:
526+
527+
```java
528+
// generics/IterableFibonacci.java
529+
// Adapt the Fibonacci class to make it Iterable
530+
import java.util.*;
531+
532+
public class IterableFibonacci
533+
extends Fibonacci implements Iterable<Integer> {
534+
private int n;
535+
public IterableFibonacci(int count) { n = count; }
536+
537+
@Override
538+
public Iterator<Integer> iterator() {
539+
return new Iterator<Integer>() {
540+
@Override
541+
public boolean hasNext() { return n > 0; }
542+
@Override
543+
public Integer next() {
544+
n--;
545+
return IterableFibonacci.this.get();
546+
}
547+
@Override
548+
public void remove() { // Not implemented
549+
throw new UnsupportedOperationException();
550+
}
551+
};
552+
}
553+
554+
public static void main(String[] args) {
555+
for(int i : new IterableFibonacci(18))
556+
System.out.print(i + " ");
557+
}
558+
}
559+
```
560+
561+
输出结果:
562+
563+
```java
564+
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584
565+
```
566+
567+
在循环语句中使用 `IterableFibonacci`,必须在构造函数中提供一个边界值,这样 `hasNext()` 才知道何时返回 **false**,结束循环。
365568

366569
<!-- Generic Methods -->
570+
367571
## 泛型方法
368572

369573

0 commit comments

Comments
 (0)