ITEM 31: USE BOUNDED WILDCARDS TO INCREASE API FLEXIBILITY 如 item 28 所述,参数化类型是不变的。换句话说,对于任何两种类型 Type1 和Type2, List 既不是 List 的子类型,也不是 List 的超类型。虽然 List 不是 List
public class Stack {
public Stack();
public void push(E e);
public E pop();
public boolean isEmpty();
}
假设我们想添加一个方法,它接受一系列元素并将它们全部推入堆栈。这是第一次尝试:
// pushAll method without wildcard type - deficient!
public void pushAll(Iterable src) {
for (E e : src)
push(e);
}
如果您尝试根据前面显示的 popAll 版本编译此客户机代码,您将得到一个与我们在pushAll 的第一个版本中得到的错误非常相似的错误: “Collection is not a subtype of Collection”。通配符类型再次提供了一种解决方法。popAll 的输入参数的类型不应该是“E的集合”,而应该是“某个超类型E的集合”(定义超类型时,E本身就是超类型[JLS, 4.10])。同样,有一个通配符类型,它的确切含义是: Collection super E>,让我们现在来修改 popAll 吧:
// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection super E> dst) {
while (!isEmpty())
dst.add(pop());
}
通过此更改,Stack 和客户机代码都可以干净地编译。教训很明显。为了获得最大的灵活性,对表示生产者或消费者的输入参数使用通配符类型。如果输入参数同时是生产者和消费者,那么通配符类型对您没有任何好处: 您需要精确的类型匹配,这是在没有通配符的情况下得到的。下面是一个助记符,帮助您记住使用哪种通配符类型: " PECS stands for producer-extends, consumer-super. " 换句话说,如果参数化类型表示一个 T 生产者,那么使用 extends T>;如果它表示一个 T 消费者,使用 super T>。在我们的堆栈示例中,pushAll 的 src参数生成供堆栈使用的 E 实例,因此 src 的适当类型是 Iterable extends E>;popAll 的 dst参数使用堆栈中的 E 个实例,因此 dst 的类型应当是 Collection super E>。PECS助记符捕获了指导通配符类型使用的基本原则。Naftalin和Wadler将其称为Get and Put原则[Naftalin 07,2.4]。 记住这个助记符之后,让我们看看本章前面几项中的一些方法和构造函数声明。 item 28中的 Chooser 构造函数有如下声明: public Chooser(Collection choices) 这个构造函数只使用集合选项来生成 T 类型的值(并将它们存储起来供以后使用),因此它的声明应该使用扩展T的通配符类型。下面是生成的构造函数声明:
// Wildcard type for parameter that serves as an T producer
public Chooser(Collection extends T> choices)
这种改变在实践中会有什么不同吗?是的,它会。假设您有一个列表,您希望将它传递给选择器的构造函数。这将不会使用原始声明编译,但是一旦将有界通配符类型添加到声明中,就会编译。 现在让我们看看item 30中的 union 方法。声明如下: public static Set union(Set s1, Set s2) 参数 s1 和 s2 都是E生产者,因此 PECS 助记符告诉我们声明应该如下: public static Set union(Set extends E> s1, Set extends E> s2) 注意,返回类型仍然设置为 Set 。不要使用有界通配符类型作为返回类型。它将迫使用户在客户机代码中使用通配符类型,而不是为用户提供额外的灵活性。使用修订后的声明,本程式码可清晰地编译:
Set integers = Set.of(1, 3, 5);
Set doubles = Set.of(2.0, 4.0, 6.0);
Set numbers = union(integers, doubles);
如果使用得当,通配符类型对类的用户几乎是不可见的。它们使得方法接受它们应该接受的参数,并拒绝它们应该拒绝的参数。如果类的用户必须考虑通配符类型,那么它的API可能有问题。 在Java 8之前,类型推断规则还不足以处理前面的代码片段,这要求编译器使用上下文指定的返回类型(或目标类型)来推断e的类型。前面显示的 union 调用的目标类型设置为 Set 。如果您尝试在早期版本的Java中编译这个片段(用适当的 Set.of 工厂替代),您将得到一个长而复杂的错误消息,如下所示: “Union.java:14: error: incompatible types Set numbers = union(integers, doubles); required: Set found: Set where INT#1,INT#2 are intersection types: INT#1 extends Number,Comparable extends INT#2> INT#2 extends Number,Comparable>” 幸运的是,有一种方法可以处理这种错误。如果编译器没有推断出正确的类型,您总是可以通过显式类型参数[JLS, 15.12]告诉它使用哪种类型。甚至在Java 8中引入目标类型之前,您也不需要经常这样做,这很好,因为显式类型参数不是很漂亮。通过添加显式类型参数(如下所示),代码片段可以在Java 8之前的版本中干净地编译:
// Explicit type parameter - required prior to Java 8
Set numbers = Union.union(integers, doubles);
接下来,让我们将注意力转向 item 30中的 max 方法。以下是原始声明: public static > T max(List list) 下面是使用通配符类型的修订声明: public static > T max( List extends T> list) 为了从原来的声明中得到修改后的声明,我们使用了两次 PECS 启发式。最简单的应用程序是参数列表。它生成了T个实例,所以我们将类型从 List 更改为 List extends T>。棘手的应用程序是类型参数 T。这是我们第一次看到将通配符应用于类型参数。最初,T被指定为扩展 Comparable,但是 T 的 Comparable 使用 T 实例(并生成指示顺序关系的整数)。因此,参数化类型 Comparable 被替换为有界通配符 Comparable super T>。Comparable 对象总是消费者,所以一般应该使用Comparable super T> 而不是 Comparable。Comparator 也是如此,应当使用 Comparator super T> 而不是 Comparator。 修订后的max声明可能是本书中最复杂的方法声明。增加的复杂性真的能给你带来什么好处吗? 再一次,是的。以下是一个简单的列表例子,原声明将排除该列表,但修订后的声明允许该列表: List> scheduledFutures = ... ; 不能将原始方法声明应用于此列表的原因是 ScheduledFuture 没有实现 Comparable。相反,它是Delayed的子接口,它继承了Comparable。换句话说,ScheduledFuture 实例不仅仅可以与其他ScheduledFuture 实例进行比较;它可以与任何延迟的实例进行比较,这足以导致原始声明拒绝它。更一般地说,通配符需要支持不直接实现 Comparable(或Comparator)但扩展了实现 Comparable(或Comparator) 的类型。 还有一个与通配符相关的主题值得讨论。类型参数和通配符之间存在对偶性,可以使用其中一个或另一个声明许多方法。例如,下面是静态方法的两种可能声明,用于交换列表中两个索引项。第一个使用无界类型参数(Item 30),第二个使用无界通配符:
// Two possible declarations for the swap method
public static void swap(List list, int i, int j);
public static void swap(List> list, int i, int j);
public static void swap(List> list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
试图编译它会产生这样一个无益的错误信息: "Swap.java:5: error: incompatible types: Object cannot be converted to CAP#1 list.set(i, list.set(j, list.get(i))); where CAP#1 is a fresh type-variable: CAP#1 extends Object from capture of ?" 我们不能把一个元素放回我们刚取出来的列表中,这似乎是不对的。问题是list的类型lList>,您不能将除null之外的任何值放入列表。幸运的是,有一种方法可以实现这种方法,而不用使用不安全的强制转换或原始类型。其思想是编写一个私有helper方法来捕获通配符类型。为了捕获类型,helper方法必须是泛型方法。它是这样的:
public static void swap(List> list, int i, int j) {
swapHelper(list, i, j);
}
// Private helper method for wildcard capture
private static void swapHelper(List list, int i, int j) {
list.set(i, list.set(j, list.get(i)));
}
package gaodai.matrix;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Sc
Asynchronous Http Client是android中非常好的异步请求工具
除了异步之外还有很多封装比如json的处理,cookie的处理
引用
Persistent Cookie Storage with PersistentCookieStore
This library also includes a PersistentCookieStore whi
安装Apache问题:系统找不到指定的文件 No installed service named "Apache2"
每次到这一步都很小心防它的端口冲突问题,结果,特意留出来的80端口就是不能用,烦。
解决方法确保几处:
1、停止IIS启动
2、把端口80改成其它 (譬如90,800,,,什么数字都好)
3、防火墙(关掉试试)
在运行处输入 cmd 回车,转到apa
问题描述:
MongoDB在非正常情况下关闭时,可能会导致索引文件破坏,造成数据在更新时没有反映到索引上。
解决方案:
使用脚本,重建MongoDB所有表的索引。
var names = db.getCollectionNames();
for( var i in names ){
var name = names[i];
print(name);
Zookeeper重载了几个构造函数,其中构造者可以提供参数最多,可定制性最多的构造函数是
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolea
本文转自:http://hatemysql.com/2010/06/29/select-into-outfile-access-deny%E9%97%AE%E9%A2%98/
为应用建立了rnd的帐号,专门为他们查询线上数据库用的,当然,只有他们上了生产网络以后才能连上数据库,安全方面我们还是很注意的,呵呵。
授权的语句如下:
grant select on armory.* to rn
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
if (PHP_SAPI == 'cli')
die('This example should only be run from a Web Brows
1. I see. 我明白了。2. I quit! 我不干了!3. Let go! 放手!4. Me too. 我也是。5. My god! 天哪!6. No way! 不行!7. Come on. 来吧(赶快)8. Hold on. 等一等。9. I agree。 我同意。10. Not bad. 还不错。11. Not yet. 还没。12. See you. 再见。13. Shut up!
基本事务的使用:
从账户一的余额中转100到账户二的余额中去,如果账户二不存在或账户一中的余额不足100则整笔交易回滚
select * from account;
-- 创建一张账户表
create table account(
-- 账户ID
id number(3) not null,
-- 账户名称
nam