第27条:优先考虑泛型方法

术语:

 递归类型限制:通过某个包含该类型参数本身的表达式来限制类型参数。


        考虑如下的方法,它的作用是返回两个集合的联合:

// Users raw types - unaccepable!
public static Set union(Set s1, Set s2) {
	Set result = new HashSet(s1);
	result.addAll(s2);
	return result;
}
        编译这段代码会产生警告,为了修正这些警告(在新代码中不应该直接使用原始类型,当前是为了举例子)要将方法声名修改为声明一个类型参数,表示这三个元素类型(两个参数及一个返回值),并在方法中使用类型参数。声名类型参数的类型参数列表,处在方法的修饰符及其返回类型之间,修改后的代码如下:
// Generic method
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
	Set<E> result = new HashSet<E>(s1);
	result.addAll(s2);
	return result;
}
        当前这个版本的union方法即为一般的泛型方法,但是它有一个限制,要求三个集合的类型(两个输入参数及一个返回值)必须全部相同。利用有限制的通配符类型可以使这个方法变得更回灵活。

        泛型方法的一个显著特征是,无需明确指定类型参数的值,不像调用泛型构造器的时候是必须要指定类型参数的,在为泛型方法的类型会存在一个类型推导的过程。编译器通过检查方法参数的类型来计算类型的值。在调用 泛型构造器的时候,要明确传递类型参数的值可能有点麻烦。类型参数出现在了变量的声明的左右两边,显得冗余:

// Parameterized type instance creation with constructor
Map<String, List<String>> anagrams = 
                 new HashMap<String, List<String>>();
        对于这情况 ,可以遵照第一条,提供一个静态工厂方法来简化:

// Generic static factory method
public static <K, V> HashMap<K, V> newHashMap() {
	return new HashMap<K, V>();
}
        利用上面的静态工厂方法,我们可以把变量声明右侧的参数类型省略掉,当参数类型多而复杂时尤其有效。

        有时会需要创建不可变但是又适合于许多不同类型的对象,由于泛型是通过擦除来实现的,可以给所有的必要的类型参数使用同一个单个对象,但是需要一个静态的工厂方法来给每个必要的类型参数分发对象。这种模式叫做“泛型单例工厂”,这种模式最常用于函数的对象。假设有一个接口,描述了一个方法,该方法接受和返回某个类型T的值:

public interface UnaryFunction<T> {
	T apply(T arg);
}
        现在假设要提供一个恒等函数,如果 在每次需要的时候都重新创建一个这样会很浪费,因为它是无状态的。如果泛型被具体化,那个每个类型都必须持有相应类型的桓等函数,但是在运行时擦除类型信息后,它们并没有什么区别,所以在这种情况下,只需要一个泛型单例就够了。请看以下示例:

// Generic singleton factory pattern
private static UnaryFunction<Object> INDENTITY_FUNCTION =
     new UnaryFunction<Object> {
	     public Object apply(Object arg) { return arg; }
     };
     
// IDENTITY_FUNCTION is stateless and its type parameter is
// unbounded so it's safe to share one instance across all types.
@SuppressWarnings("unchecked")
public static <T> UnaryFunction<T> indentityFunction() {
	return (UnaryFunction<T>)INDENTITY_FUNCTION;
}
        递归类型限制最普遍的用途与Comparable接口有关,它定义类型的自然顺序。许多 方法都带有一个实现Comparable接口的元素列表,为了对列表进行排序,并在其中进行搜索,计算出它的最小值或者最大值等等。要完成这其中的任何一项工作要求列表中的每个元素都能够与列表中的其他元素相比较,下面是如何表达这种约束条件的一个示例:

// Using a recursive type bound to express mutual comparability
public static <T extends Comparable<T>> T max(List<T> list) {
	...
}
        上面的声名要求T是可以与自身同类型对象相比较的类型,extends可以解释为"实现某功能"。

        总之,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来的更加安全,也更加容易。就像类型一样,你应该确保新的方法可以不用转换就能使用,这通常意味着要将它们泛型化。并且就像类型一样,还应该将现有的方法泛型化,使新用户使用起来更加轻松,且不会破坏现有的客户端。





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面我将为你介绍如何在 Java 中建立泛型泛型方法,并进行调用。 首先,让我们看一下如何建立泛型泛型是指具有一个或多个型参数的。我们可以使用型参数来代替具体的数据型,从而使更通用、更灵活。例如,下面是一个简单的泛型示例: ```java public class Pair<T1, T2> { private T1 first; private T2 second; public Pair(T1 first, T2 second) { this.first = first; this.second = second; } public T1 getFirst() { return first; } public T2 getSecond() { return second; } } ``` 在上面的示例中,我们定义了一个 `Pair` ,该有两个型参数 `T1` 和 `T2`。我们可以使用这个来表示一对值,其中一个值的型是 `T1`,另一个值的型是 `T2`。 接下来,让我们看一下如何建立泛型方法泛型方法是指具有一个或多个型参数的方法。我们可以使用型参数来代替具体的数据型,从而使方法更通用、更灵活。例如,下面是一个简单的泛型方法示例: ```java public class Utils { public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); } } ``` 在上面的示例中,我们定义了一个 `printArray` 方法,该方法有一个型参数 `T`。我们可以使用这个方法来打印任何型的数组。 现在,我们来看一下如何调用泛型泛型方法。对于泛型,我们需要在创建对象时指定型参数。例如: ```java Pair<Integer, String> p1 = new Pair<>(1, "hello"); Pair<Double, Double> p2 = new Pair<>(3.14, 2.71); ``` 在上面的示例中,我们创建了两个 `Pair` 对象,一个用于存储一个 `Integer` 和一个 `String`,另一个用于存储两个 `Double`。 对于泛型方法,我们需要在调用方法时指定型参数。例如: ```java Integer[] arr1 = {1, 2, 3, 4, 5}; String[] arr2 = {"hello", "world"}; Utils.printArray(arr1); // 输出:1 2 3 4 5 Utils.printArray(arr2); // 输出:hello world ``` 在上面的示例中,我们调用了 `printArray` 方法,并传递了两个不同型的数组作为参数。由于该方法泛型方法,因此编译器会根据传递的参数型自动推断出型参数 `T` 的具体型。 希望这个示例能够帮到你,祝你学习愉快!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值