7.2、泛型类型

这篇Java教程基于JDK1.8。教程中的示例和实践不会使用未来发行版中的优化建议。
泛型类型

泛型类型是参数化于类型之上的泛型类或接口。接下来的Box类将修改用于演示这个概念:

简单的Box

首先提供一个对任何类型的对象进行操作的非泛型Box类。它只需要提供两个方法:set(添加对象)和get(检索对象):

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

因为它的方法接受或返回一个对象,所以你可以自由地传递任何你想要的内容,只要它不是基础类型之一。在编译时是没有任何方法来检测类是如何使用的。代码的一部分可能将整数放入Box中,并期望从中获得整数,而另一部分可能错误地传入字符串,从而导致运行时错误。

泛型版本的Box

泛型类通过下面的格式定义:

class name<T1, T2, ..., Tn> { /* ... */ }

类型参数部分由尖括号(<>)分隔,位于类名之后。它指定类型参数(也称为类型变量)T1、T2、…和Tn。

要更新Box类以使用泛型,可以通过将代码*“public class Box”更改为“public class Box”* 来创建泛型类型声明。这引入了类型变量T,它可以在类内的任何地方使用。

通过这种改变,Box类变成了:

/**
* Generic version of the Box class.
* @param <T> the type of the value being boxed
*/
public class Box<T> {
   // T stands for "Type"
   private T t;

   public void set(T t) { this.t = t; }
   public T get() { return t; }
}

如你所见,所有Object出现的地方都替换成了T。类型变量可以是你指定的任何非基本类型:任何类类型、任何接口类型、任何数组类型,甚至是另一个类型变量。

同样的技术可以用在创建泛型接口。

类型参数的命名约定

按照约定,类型参数名称是单个大写字母。这与你已经了解的变量命名约定形成了鲜明的对比,并且有充分的理由相信:如果没有这种约定,就很难区分类型变量和普通类或接口名称。

最常见的类型参数命名如下:

  • E 元素
  • K Key
  • N Number
  • T Type
  • V Value
  • S、U、V等等 - 第二、第三和第四类型

你将在整个Java SE API和本教程的其余部分中看到这些名称。

调用并实例化一个泛型

要从代码中引用泛型Box类,必须执行泛型类型调用,它将T替换为具体的值,比如Integer

Box<Integer> integerBox;

你可以将泛型类型调用看作与普通方法调用类似,但你不是将参数传递给方法,而是将类型参数(在本例中为Integer)传递给Box类本身。

与其他变量声明一样,这段代码实际上并不创建新的Box对象。它简单地声明integerBox将保存对*“Box<Integer>”的引用,这就是读取Box<Integer>*的方式。

泛型类型调用通常称为参数化类型。

要实例化这个类,像往常一样使用new关键字,但是在类名和括号之间放置<Integer>:

Box<Integer> integerBox = new Box<Integer>();
菱形

在Java SE 7及以后的版本中,只要编译器能够从上下文中确定或推断类型参数,就可以将调用泛型类构造函数所需的类型参数替换为类型参数的空集(<>)。这对角括号<>,被非正式地称为菱形。例如,你可以使用以下语句创建Box<Integer>的实例:

Box<Integer> integerBox = new Box<>();
多类型参数

如前所述,泛型类可以有多个类型参数。例如,通用的OrderedPair类,它实现了通用的Pair接口:

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
	this.key = key;
	this.value = value;
    }

    public K getKey()	{ return key; }
    public V getValue() { return value; }
}

下面的语句创建了OrderedPair类的两个实例:

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");

这段代码new OrderedPair<String, Integer>,它将K实例化为字符串,将V实例化为整数。因此,OrderedPair构造函数的参数类型分别是String和Integer。由于自动装箱,所以向类传递一个字符串和一个整数是有效的。

在上面讲过,由于Java编译器可以从OrderedPair<String, Integer>声明中推导出KV 的类型,这些语句可以简化为:

OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String>  p2 = new OrderedPair<>("hello", "world");

为创建泛型接口,遵从创建泛型类的同样约定即可。

参数化类型

你还可以用参数化类型(即、列表<字符串>)来替换类型参数(即, K或V)。例如,使用OrderedPair<K, V>的例子:

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值