方法重载是Java和其他面向对象语言最具特色的特性之一。当许多人可能认为Java的优势是它的类型,或者是它所带的API库,其实让相同的方法名与各种各样可接受的参数搭配也是一件很好的事。
Guitar guitar = new Guitar("Bourgeois", "Country Boy Deluxe",
GuitarWood.MAHOGANY, GuitarWood.ADIRONDACK,1.718);
Guitar guitar = new Guitar("Martin", "HD-28");
Guitar guitar = new Guitar("Collings", "CW-28"
GuitarWood.BRAZILIAN_ROSEWOOD, GuitarWood.ADIRONDACK,1.718,
GuitarInlay.NO_INLAY, GuitarInlay.NO_INLAY);
This code calls three versions of the constructor of a (fictional) Guitar class, meaning that information can be supplied when it’s available,rather than forcing a user to know everything about their guitar at one time (many professionals couldn’t tell you their guitar’s width at the nut).
Here are the constructors used:
public Guitar(String builder, String model) {
}
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,
float nutWidth) {
}
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,
float nutWidth,
GuitarInlay fretboardInlay, GuitarInlay topInlay) {
}
这段代码调用了Guitar类中三个版本的构造器,意味着当信息可见时,这些信息会被支持,而不是迫使每一个使用者每一次都要去了解关于Guitar类的所有知识。许多专家不会在关键时候告诉你他们的Guitar的内容。下面是用到的构造器:
public Guitar(String builder, String model) {
}
public Guitar(String builder, String model,GuitarWood backSidesWood, GuitarWood topWood,float nutWidth) {
}
public Guitar(String builder, String model,GuitarWood backSidesWood, GuitarWood topWood,float nutWidth,
GuitarInlay fretboardInlay, GuitarInlay topInlay) {
}
然而,当你想要去增加无限的信息时,事情开始变得有一点不是那么有用了。例如:假设你想允许在这个构造器中增加额外的未指明的特性。下面就是一些可能的调用的例子:
Guitar guitar = new Guitar("Collings", "CW-28"
GuitarWood.BRAZILIAN_ROSEWOOD, GuitarWood.ADIRONDACK,1.718,
GuitarInlay.NO_INLAY, GuitarInlay.NO_INLAY,"Enlarged Soundhole", "No Popsicle Brace");
Guitar guitar = new Guitar("Martin", "HD-28V","Hot-rodded by Dan Lashbrook", "Fossil Ivory Nut","Fossil Ivory Saddle", "Low-profile bridge pins");
对于这两个单独的情况,你不得不去增加一个构造器来接受两个额外的字符串,另外一个构造器来接受四个额外的字符串。试图将这些相似的版本应用于早已重载的构造器。根据这样的话,你最终会得到20或30个那样愚蠢的构造器的版本!
原因在于我们常称做的可变参数。可变参数是Tiger的增加的另一个特性,它用一种相当巧妙的方法彻底地解决了这儿提出的问题。这一章讲述了这种相对简单的特性的各个方面。这将会使你迅速写出更好、更整洁、更灵活的代码。
创建一个可变长度的参数列表
可变参数使得你可以指定某方法来接受多个同一类型的参数,而且并不要求事先确定参数的数量(在编译或运行时)。
这就是Tiger的一个集成部分。事实上,正是因为Java语言的一些新特性组合在一起才表现出了可变参数的特性。
我如何去实现呢?
首先,你要习惯的书写省略号(……)。这三个小点是可变参数的关键,你将会经常键入它们。下面是Guitar类的构造器使用可变参数来接受不确定数量字符串的一个例子:
public Guitar(String builder, String model, String……features);
参数String…… features 表明任何数量的字符串都可能被接受。 所以,下面所有的调用都合法的。
Guitar guitar = new Guitar("Martin", "HD-28V","Hot-rodded by Dan Lashbrook", "Fossil Ivory Nut","Fossil Ivory Saddle", "Low-profile bridge pins");
Guitar guitar = new Guitar("Bourgeois", "OMC","Incredible flamed maple bindings on this one.");
Guitar guitar = new Guitar("Collings", "OM-42","Once owned by Steve Kaufman--one of a kind");
You could add the same variable-length argument to the other constructors:
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,float nutWidth, String... features)
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,float nutWidth,
GuitarInlay fretboardInlay,GuitarInlay topInlay,String... features)
例5-1描写了一个把所有的这些特性放在一起的简单类,甚至使用XX来一起传递一些可变参数。
Example 5-1. Using varargs in constructors
package com.oreilly.tiger.ch05;
public class Guitar {
private String builder;
private String model;
private float nutWidth;
private GuitarWood backSidesWood;
private GuitarWood topWood;
private GuitarInlay fretboardInlay;
private GuitarInlay topInlay;
private static final float DEFAULT_NUT_WIDTH = 1.6875f;
public Guitar(String builder, String model, String... features) {
this(builder, model, null, null, DEFAULT_NUT_WIDTH, null, null, features);
}
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,
float nutWidth, String... features) {
this(builder, model, backSidesWood, topWood, nutWidth, null, null, features);
}
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,float nutWidth,
GuitarInlay fretboardInlay, GuitarInlay topInlay,String... features) {
this.builder = builder;
this.model = model;
this.backSidesWood = backSidesWood;
this.topWood = topWood;
this.nutWidth = nutWidth;
this.fretboardInlay = fretboardInlay;
this.topInlay = topInlay;
}
}
刚才发生了什么?
当你指定了一个可变长度参数列表,Java编译器实际上读入 “create an array of type <参数类型>”。你键入:
public Guitar(String builder, String model, String…… features)
然而:编译器解释这些为:
public Guitar(String builder, String model, String[] features)
这意味着重复参数列表变得简单(这将在“重复可变长度参数列表”里讲述),这与你需要完成的其他程序设计目标是一样。
你可以像使用数组一样来使用可变参数。
然而,这同样存在一些限制。第一,在每个方法中,你只可以使用一次省略号。所以,下面的书写是不合法的:
public Guitar(String builder, String model,String…… features, float…… stringHeights)
另外,省略号必须作为方法的最后一个参数。
如果你不需要传递任何可变参数呢?
那没关系,你只需要以旧的方式调用构造器:
Guitar guitar = new Guitar("Martin", "D-18");
我们再仔细看看,虽然程序中没有与下面代码相匹配的构造器:
public Guitar(String builder, String model)
那么,代码到底传递了什么呢?作为可变参数的特例,在参数中不传递东西是一个合法的选项。所以,当你看到 String…… features,你应该把它认为是零个或者更多个String参数。这省却你再去创建另一个不带可变参数构造器的麻烦。
重复可变长度参数类表
所有这些可变参数是很好的。但是实际上,如果你不在你的方法中使用它们的话,他们显然仅仅是吸引眼球的东西或是窗户的装饰品而已。
然而,你可以像你使用数组一样来使用可变参数,你会觉得这种用法很简单。
那我怎么来使用可变参数呢?
首先你要确保阅读了“创建一个可变长度的参数列表”,你会从中了解到可变参数方法最重要的东西,那就是我们把可变参数当作数组来看待。
所以,继续前面的例子,你可以写出下面的代码:
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,float nutWidth,
GuitarInlay fretboardInlay, GuitarInlay topInlay,String... features) {
this.builder = builder;
this.model = model;
this.backSidesWood = backSidesWood;
this.topWood = topWood;
this.nutWidth = nutWidth;
this.fretboardInlay = fretboardInlay;
this.topInlay = topInlay;
for (String feature : features) {
System.out.println(feature);
}
}
上面的这段代码看上是不是不是那么的有吸引力?但这确实体现了可变参数的精髓。作为另一个例子,下面这个简单的方法从一组数字中计算出最大值:
public static int max(int first, int... rest) {
int max = first;
for (int i : rest) {
if (i > max)
max = i;
}
return max;
}
是不是,够简单吧?
那么如何存储可变长度参数呢?
正因为Java编译器把这些看作数组,所以数组显然是一个存储的好选择,这将在下面的例5-2中体现。
Example 5-2. 存储作为成员变量的可变参数
package com.oreilly.tiger.ch05;
public class Guitar {
private String builder;
private String model;
private float nutWidth;
private GuitarWood backSidesWood;
private GuitarWood topWood;
private GuitarInlay fretboardInlay;
private GuitarInlay topInlay;
private String[] features;
private static final float DEFAULT_NUT_WIDTH = 1.6875f;
public Guitar(String builder, String model, String... features) {
this(builder, model, null, null, DEFAULT_NUT_WIDTH, null, null, features);
}
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,
float nutWidth, String... features) {
this(builder, model, backSidesWood, topWood, nutWidth, null, null, features);
}
public Guitar(String builder, String model,
GuitarWood backSidesWood, GuitarWood topWood,
float nutWidth,
GuitarInlay fretboardInlay, GuitarInlay topInlay,
String... features) {
this.builder = builder;
this.model = model;
this.backSidesWood = backSidesWood;
this.topWood = topWood;
this.nutWidth = nutWidth;
this.fretboardInlay = fretboardInlay;
this.topInlay = topInlay;
this.features = features;
}
}
你可以简单地在Java的Collection类中存储这些可变参数。
//变量声明
private