JAVA通配符

上限通配符  

我们想要的是一个确切元素类型未知的列表,这一点与数组是不同的。

List<Number>是一个列表,其元素类型是具体类型Number。

List<? extends Number>是一个确切元素类型未知的列表。它是Number或其子类型。

上限  

如果我们更新初始的例子,并赋值给List<? extends Number>,那么现在赋值就会成功了:

 List<Integer> iList = new ArrayList<Integer>();

List<? extends Number> nList = iList;

Number n = nList.get(0);nList.add(0.5); // Not allowed   

我们可以从列表中得到Number,因为无论列表的确切元素类型是什么(Float、Integer或Number),我们都可以把它赋值给Number。   

我们仍然不能把浮点类型插入列表中。这会在编译时失败,因为我们不能证明这是安全的。如果我们想要向列表中添加浮点类型,它将破坏iList的初始类型安全——它只存储Integer。   

通配符给了我们比数组更多的表达能力。

为什么使用通配符  

在下面这个例子中,通配符用于向API的用户隐藏类型信息。在内部,Set被存储为CustomerImpl。而API的用户只知道他们正在获取一个Set,从中可以读取Customer。

此处通配符是必需的,因为无法从Set<CustomerImpl>向Set<Customer>赋值:

public class CustomerFactory {   

private Set<CustomerImpl> _customers;   

 public Set<? extends Customer> getCustomers() {       

 return _customers;   

 }}

通配符和协变返回  

通配符的另一种常见用法是和协变返回一起使用。与赋值相同的规则可以应用到协变返回上。如果希望在重写的方法中返回一个更具体的泛型类型,声明的方法必须使用通配符:

public interface NumberGenerator {   

 public List<? extends Number> generate();

}

public class FibonacciGenerator extends NumberGenerator {   

public List<Integer> generate() {        ...    }}

 如果要使用数组,接口可以返回Number[],而实现可以返回Integer[]。

下限   

我们所谈的主要是关于上限通配符的。还有一个下限通配符。

List<? super Number>是一个确切“元素类型”未知的列表,但是可能是Mnumber,或者Number的超类型。所以它可能是一个List<Number>或一个List<Object>。   

下限通配符远没有上限通配符那样常见,但是当需要它们的时候,它们就是必需的。

下限与上限

 List<? extends Number> readList = new ArrayList<Integer>();

Number n = readList.get(0);

List<? super Number> writeList = new ArrayList<Object>();

writeList.add(new Integer(5));   

第一个是可以从中读数的列表。   第二个是可以向其写数的列表。

无界通配符  

最后,List<?>列表的内容可以是任何类型,而且它与List<? extends Object>几乎相同。可以随时读取Object,但是不能向列表中写入内容。

公共API中的通配符   

总之,正如前面所说,通配符在向调用程序隐藏实现细节方面是非常重要的,但即使下限通配符看起来是提供只读访问,由于remove(int position)之类的非泛型方法,它们也并非如此。如果您想要一个真正不变的集合,可以使用java.util.Collection上的方法,比如unmodifiableList()。   

编写API的时候要记得通配符。通常,在传递泛型类型时,应该尝试使用通配符。它使更多的调用程序可以访问API。   

通过接收List<? extends Number>而不是List<Number>,下面的方法可以由许多不同类型的列表调用: void removeNegatives(List<? extends Number> list); 构造泛型类型  

现在我们将讨论构造自己的泛型类型。我们将展示一些例子,其中通过使用泛型可以提高类型安全性,我们还将讨论一些实现泛型类型时的常见问题。集合风格(Collection-like)的函数  

第一个泛型类的例子是一个集合风格的例子。Pair有两个类型参数,而且字段是类型的实例:

public final class Pair<A,B> {   

 public final A first;   

public final B second;    

public Pair(A first, B second) {       

 this.first = first;       

 this.second = second;   

}}   

这使从方法返回两个项而无需为每个两种类型的组合编写专用的类成为可能。另一种方法是返回Object[],而这样是类型不安全或者不整洁的。 在下面的用法中,我们从方法返回一个File和一个Boolean。方法的客户端可以直接使用字段而无需类型强制转换:

public Pair<File,Boolean> getFileAndWriteStatus(String path){   

 // create file and status    return new Pair<File,Boolean>(file, status);

}

 Pair<File,Boolean> result = getFileAndWriteStatus("...");

File f = result.first;boolean writeable = result.second;

 集合之外   

在下面这个例子中,泛型被用于附加的编译时安全性。通过把DBFactory类参数化为所创建的Peer类型,您实际上是在强制Factory子类返回一个Peer的特定子类型:

public abstract class DBFactory<T extends DBPeer> {   

protected abstract T createEmptyPeer();   

public List<T> get(String constraint) {       

 List<T> peers = new ArrayList<T>();       

 // database magic       

return peers;   

}}

通过实现DBFactory<Customer>,CustomerFactory必须从createEmptyPeer()返回一个Customer:public class CustomerFactory extends DBFactory<Customer>{     public Customer createEmptyPeer() {        return new Customer();    }}

泛型方法  

不管想要对参数之间还是参数与返回类型之间的泛型类型施加约束,都可以使用泛型方法:

   例如,如果编写的反转函数是在位置上反转,那么可能不需要泛型方法。然而,如果希望反转返回一个新的List,那么可能会希望新List的元素类型与传入的List的类型相同。在这种情况下,就需要一个泛型方法: <T> List<T> reverse(List<T> list) 具体化  

当实现一个泛型类时,您可能想要构造一个数组T[]。因为泛型是通过擦除(erasure)实现的,所以这是不允许的。   

您可以尝试把Object[]强制转换为T[]。但这是不安全的。

具体化解决方案  

按照泛型教程的惯例,解决方案使用的是“类型令牌”,通过向构造函数添加一个Class<T>参数,可以强制客户端为类的类型参数提供正确的类对象:

public class ArrayExample<T> {   

private Class<T> clazz;    

public ArrayExample(Class<T> clazz) {       

this.clazz = clazz;    }    

public T[] getArray(int size) {       

 return (T[])Array.newInstance(clazz, size);   

 }}

  为了构造ArrayExample<String>,客户端必须把String.class传递给构造函数,因为String.class的类型是Class<String>。 拥有类对象使构造一个具有正确元素类型的数组成为可能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值