mirror of
https://github.com/Jueee/effective-Java.git
synced 2025-03-14 03:10:42 +08:00
优先使用泛型方法
This commit is contained in:
parent
a17ea15649
commit
7731ede3aa
@ -12,4 +12,8 @@
|
||||
|
||||
绝大多数泛型类型就像我们的 `Stack` 示例一样,它们的类型参数没有限制:可以创建一个 `Stack<Object>,Stack<int[]>`,`Stack<List<String>>` 或者其他任何对象的 `Stack` 引用类型。 请注意,不能创建基本类型的堆栈:尝试创建 `Stack<int>` 或 `Stack<double>` 将导致编译时错误。 这是 Java 泛型类型系统的一个基本限制。 可以使用基本类型的包装类(详见第 61 条)来解决这个限制。
|
||||
|
||||
总之,泛型类型比需要在客户端代码中强制转换的类型更安全,更易于使用。 当你设计新的类型时,确保它们可以在没有这种强制转换的情况下使用。 这通常意味着使类型泛型化。 如果你有任何现有的类型,应该是泛型的但实际上却不是,那么把它们泛型化。 这使这些类型的新用户的使用更容易,而不会破坏现有的客户端(条目 26)。
|
||||
总之,泛型类型比需要在客户端代码中强制转换的类型更安全,更易于使用。 当你设计新的类型时,确保它们可以在没有这种强制转换的情况下使用。 这通常意味着使类型泛型化。 如果你有任何现有的类型,应该是泛型的但实际上却不是,那么把它们泛型化。 这使这些类型的新用户的使用更容易,而不会破坏现有的客户端(条目 26)。
|
||||
|
||||
假设你想写一个恒等方法分配器( identity function dispenser)。 类库提供了 `Function.identity` 方法,所以没有理由编写你自己的实现,但它是有启发性的。 如果每次要求的时候都去创建一个新的恒等方法对象是浪费的,因为它是无状态的。 如果 Java 的泛型被具体化,那么每个类型都需要一个恒等方法,但是由于它们被擦除以后,所以泛型的单例就足够了。
|
||||
|
||||
**示例代码**:[Item30Example02.java](Generics/src/main/java/com/jueee/item30/Item30Example02.java):泛型 `Stack` 类的使用。 该程序以相反的顺序打印其命令行参数,并将其转换为大写。
|
57
ch05泛型/30.优先使用泛型方法.md
Normal file
57
ch05泛型/30.优先使用泛型方法.md
Normal file
@ -0,0 +1,57 @@
|
||||
## 优先使用泛型方法
|
||||
|
||||
正如类可以是泛型的,方法也可以是泛型的。 对参数化类型进行操作的静态工具方法通常都是泛型的。
|
||||
|
||||
集合中的所有“算法”方法(如 `binarySearch` 和 `sort`)都是泛型的。
|
||||
|
||||
### 编写泛型方法
|
||||
|
||||
**示例代码**:[Item30Example01.java](Generics/src/main/java/com/jueee/item30/Item30Example01.java):泛型方法示例,取两个集合的并集。
|
||||
|
||||
对于简单的泛型方法来说,就是这样。
|
||||
|
||||
泛型方法编译时不会生成任何警告,并提供类型安全性和易用性。 这是一个简单的程序来运行该方法。 这个程序不包含强制转换和编译时没有错误或警告:至少对于简单的泛型方法来说,就是这样。
|
||||
|
||||
### 恒等方法分配器
|
||||
|
||||
假设你想写一个恒等方法分配器( identity function dispenser)。
|
||||
|
||||
类库提供了 `Function.identity` 方法,所以没有理由编写你自己的实现,但它是有启发性的。
|
||||
|
||||
如果每次要求的时候都去创建一个新的恒等方法对象是浪费的,因为它是无状态的。
|
||||
|
||||
如果 Java 的泛型被具体化,那么每个类型都需要一个恒等方法,但是由于它们被擦除以后,所以泛型的单例就足够了。
|
||||
|
||||
**示例代码**:[Item30Example02.java](Generics/src/main/java/com/jueee/item30/Item30Example02.java): 恒等方法分配器( identity function dispenser)
|
||||
|
||||
### 递归类型限制
|
||||
|
||||
类型参数受涉及该类型参数本身的某种表达式限制是允许的。 这就是所谓的递归类型限制(recursive type bound)。
|
||||
|
||||
递归类型限制的常见用法与 `Comparable` 接口有关,它定义了一个类型的自然顺序。
|
||||
|
||||
```java
|
||||
public interface Comparable<T> {
|
||||
int compareTo(T o);
|
||||
}
|
||||
```
|
||||
|
||||
类型参数 T 定义了实现 `Comparable<T>` 的类型的元素可以比较的类型。
|
||||
|
||||
在实际中,几乎所有类型都只能与自己类型的元素进行比较。 所以,例如,`String` 类实现了 `Comparable<String>`,`Integer` 类实现了 `Comparable<Integer>` 等等。
|
||||
|
||||
**示例代码**:[Item30Example03.java](Generics/src/main/java/com/jueee/item30/Item30Example03.java):根据其元素的自然顺序来计算集合中的最大值。
|
||||
|
||||
请注意,如果列表为空,则此方法将引发 `IllegalArgumentException` 异常。 更好的选择是返回一个 `Optional<E>`。
|
||||
|
||||
限定的类型 `<E extends Comparable <E >>` 可以理解为「任何可以与自己比较的类型 E」,这或多或少精确地对应于相互可比性的概念。
|
||||
|
||||
### 总结
|
||||
|
||||
总之,像泛型类型一样,泛型方法比需要客户端对输入参数和返回值进行显式强制转换的方法更安全,更易于使用。
|
||||
|
||||
像类型一样,你应该确保你的方法可以不用强制转换,这通常意味着它们是泛型的。
|
||||
|
||||
应该泛型化现有的方法,其使用需要强制转换。
|
||||
|
||||
这使得新用户的使用更容易,而不会破坏现有的客户端。
|
@ -0,0 +1,34 @@
|
||||
package com.jueee.item30;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Item30Example01 {
|
||||
|
||||
// Uses raw types - unacceptable!
|
||||
public static Set union1(Set s1, Set s2) {
|
||||
Set result = new HashSet(s1);
|
||||
result.addAll(s2);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Generic method
|
||||
public static <E> Set<E> union2(Set<E> s1, Set<E> s2) {
|
||||
Set<E> result = new HashSet<>(s1);
|
||||
result.addAll(s2);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Simple program to exercise generic method
|
||||
public static void main(String[] args) {
|
||||
Set<String> guys = Set.of("Tom", "Dick", "Harry");
|
||||
Set<String> stooges = Set.of("Larry", "Moe", "Curly");
|
||||
|
||||
Set<String> aflCio1 = union1(guys, stooges);
|
||||
System.out.println(aflCio1);
|
||||
|
||||
Set<String> aflCio2 = union2(guys, stooges);
|
||||
System.out.println(aflCio2);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.jueee.item30;
|
||||
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
public class Item30Example02 {
|
||||
|
||||
// Generic singleton factory pattern
|
||||
private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> UnaryOperator<T> identityFunction() {
|
||||
return (UnaryOperator<T>)IDENTITY_FN;
|
||||
}
|
||||
|
||||
// Sample program to exercise generic singleton
|
||||
public static void main(String[] args) {
|
||||
String[] strings = {"jute", "hemp", "nylon"};
|
||||
UnaryOperator<String> sameString = identityFunction();
|
||||
for (String s : strings) {
|
||||
System.out.println(sameString.apply(s));
|
||||
}
|
||||
|
||||
|
||||
Number[] numbers = {1, 2.0, 3L};
|
||||
UnaryOperator<Number> sameNumber = identityFunction();
|
||||
for (Number n : numbers) {
|
||||
System.out.println(sameNumber.apply(n));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.jueee.item30;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Item30Example03 {
|
||||
|
||||
// Returns max value in a collection - uses recursive type bound
|
||||
public static <E extends Comparable<E>> E max(Collection<E> c) {
|
||||
if (c.isEmpty()) {
|
||||
throw new IllegalArgumentException("Empty collection");
|
||||
}
|
||||
E result = null;
|
||||
for (E e : c) {
|
||||
if (result == null || e.compareTo(result) > 0) {
|
||||
result = Objects.requireNonNull(e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
List<Integer> integers = Arrays.asList(11,22,43,34,35,56,25);
|
||||
System.out.println(max(integers));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user