参数类(parametric classes)在Nice中有一种非常强大的特性是可以定义参数类。参数类很像C++中的模板,或者类似其它函数型语言中的模型。而针对参数类的编程有时也被称为泛型编程。
参数类就是有参数的类,在这种情况下参数更像一种类型而不是值。你可以把参数类认为是一族相关类,这些类除了被参数化的部分以外有相同的行为和结构。参数类常被用于数据结构中。
示例3.4 简单Java集合
class Stack{
List contents = new LinkedList();
void push(Object o){
contents.add(o);
}
//... omitted methods
public static void main(String[] args){
Stack st = new Stack();
st.push("Test");
Integer num = (Integer)st.pop(); // Runtime error
}
}
这里有一个非常大的安全隐患。我将一个String压栈,然后试图将它弹入一个Integer中,这么做会导致一个运行时错误。参数类能够解决这个问题。
示例3.5 简单Nice集合
class Stack<T>{
List<T> contents = new LinkedList();
void push(T t){
contents.add(t);
}
//... omitted methods
}
void main(String[] args){
Stack<String> st = new Stack();
st.push("Test");
Integer num = st.pop(); // Compile time error!
}
在Nice版的集合中,我们有一个T类型的参数类Stack。其实Stack<T>是一种给编译器的信号,它告诉编译器如何创建一个用于给定类型的Stack。当编译器得知我们只会把String放到这个栈中时,任何企图从Stack<String>中取出一个Integer的代码都会导致编译器报错。
接口声明Nice和Java或C#一样只允许单继承,这意味着任何类只能从一个超类继承。有时候,一个类从两个(或更多)类“继承”,从它的每个父类获得行为和数据,这样可能会更好。在Nice中,这种情况可以像Java一样用接口去实现。
接口声明时就像类一样,只是接口只能包含方法,而不能包含数据成员。而和Java所不同的是,Nice中的接口也可以包含方法的默认实现,这样就比Java中的接口更方便。
虽然说了这么多还是以Java的模式来认识接口。在Nice中一个接口并不真正地包含任何东西,它只是一种标记。就像java.io.Serializable接口,只是一个标记,用来告知Java可以对一个类的实例进行序列化。Nice中所有的接口都是标记,这是因为Nice中拥有多元方法。多元方法由自己本身定义,而不包括在任何类或接口中。可以随时向接口中添加新方法,就像可以随时向类添加新方法一样。Nice基于多元方法的另一个结果是接口不光可以“包含”方法签名,还可以“包含”方法的具体实现。
Nice可以使用像Java一样的方式定义接口,就像下面的例子:
示例3.6 定义接口
interface Component{
String getID();
(int,int) getPosition();
(int,int) getDimensions();
int getArea(){
(int width, int height) = this.getDimensions();
return width * height;
}
}
当然,可以等价地使用下面这种方法定义接口:
示例3.7 在接口定义中包含全局方法
interface Component {}
String getID(Component comp);
(int,int) getPosition(Component comp);
(int,int) getDimensions(Component comp);
int getArea(Component comp){
(int width, int height) = comp.getDimensions();
return width * height;
}
事实上,混合这两种形式完全没有问题,方法可以一些定义在接口块(block)内,另一些定义在接口块外。一种较好的做法是将没有默认实现的方法定义在接口内,而有默认实现的定义在接口外。这样当其他人阅读代码时会很清楚地知道实现一个接口时那些方法必须被实现。当然,编译器会保证所有必需的方法都被实现,所以以上的内容只是一个建议。
枚举类枚举类(或简称为enum),是一组相关的常量。许多语言支持定义简单的常量,当然Nice也不例外。许多程序中使用了一些数字常量以表示特殊的含义。比如一个自动贩卖机的程序中的一个方法会像下面这样:
let int NICKEL = 5;
let int DIME = 10;
let int QUARTER = 25;
class VendingMachine{
int change = 0;
}
void addCoin(VendingMachine machine, int cents){
machine.change += cents;
}
但这个方法并不安全!它可以接受任何数量的零钱,包括一些像3或234320这样的荒谬的数字。一种处理这个问题的方法是在运行时做检查以确保数据时可以被接受的,并在遇到不可接受的数据时抛出异常。然而,在Nice中有一种更简单的解决方案:枚举。
enum class-name[(parameter-type parameter-name, ...)]{
option,...
}
枚举可以像下面这种简单的符号:
enum Color { Red, Orange
, Yellow, Blue, Indigo, Violet }
或者他们可以包含整型(或其它)的值:
enum VendingCoin(int value){
nickel(5), dime(10), quarter(25);
}
class VendingMachine{
int change = 0;
}
void addCoin(VendingMachine machine, VendingCoin cents){
machine.change += cents.value;
}
当然,一台真正的自动贩卖机会保留每一种硬币的实际数量,而不仅仅时它们的总和!