第三章 类与接口

类声明

数据成员

一个类最主要的组件使它的数据成员列表。数据成员是附属于每一个类实例的变量,它有类型、名称,可以有初始值。数据成员的声明语法如下:

type field-name[ = initial-value];

如果没有被赋予默认值,那么每一次调用构造器都必须给这个成员赋予一个指定的值。如果它拥有了默认值,对构造器的调用会覆盖那个值,在这种情况下默认值不会被计算(只有当计算会带来副作用时这一点会变得很重要)。
 

构造器

Nice中的类有一个由编译器自动生成的默认的(或者说“自动的”)构造器。这个构造器允许类中所有的事件成员被显式地初始化,但如果一个数据成员在声明时已经有了一个默认值时,它将不会出现在构造器中而是使用默认值。在大多数情况下,这个默认构造器已经可以满足需要,它把程序员从烦闷的,仅仅是接收值并将它们赋给数据成员的代码中解脱出来。
 
示例3.1 使用默认构造器声明及创建类

class Car{
    String brand;
    String model;
    int numberOfWheels = 4;
    int numberOfDrivingWheels = 2;
}

void test(){
    Car renault5 = new Car(brand: "Renault", model: "Cinq");
    Car jeep = new Car(brand: "Jeep", model: "Some jeep", numberOfDrivingWheels: 4);
}

调用构造器时必须包含数据成员的名称,这一点很重要。首先,这样不用查看类定义就能很容易理解参数代表着什么。其次,可以不用再将数据成员武断地列序1。(1在Java中当构造器参数的次序必须被改变时会出现问题。这样做必须修改所有的调用点,而这种修改在最好情况下会导致繁琐的工作和错误,而在最坏情况下是不可能实现的(当编写一个库时)。当顺序改变了而只有一部分调用点被修改后会出现以下情况:如果交换的参数类型不一致,编译无法通过;或者,编译可以通过但在运行时产生错误的结果。在Java中没有一个简单的方法能解决这个问题。在Nice中,调用构造器时使用名称则是一种解决方案。)因为数据成员的名称被显式使用,赋值时可以使用任何顺序。

当构造器中需要处理更多的控制时,可以写一个新的构造器以取代自动构造器。新构造器的写法很像其它方法,但在声明时有细微差别:

new class-name(param-typeparam-name[ = initial-value], ...){
    method-body;
    this(argument, ...);
}

语法中要求任何自定义构造器中最后一条语句必须调用同一类型的其它构造器,在大多数情况下调用的是自动构造器。
 
示例3.2 使用自定义构造器定义并创建构类

/**
 * Class which encapsulates a strategy for
 * way of translating a character.
 */
class Translator{
    //The function that actually performs the translation
    char->char transFunction;

    //convenience method
    char translate(char input){
        return (this.transFunction)(input);
    }
}

/**
 * Constructor which takes a map of characters, and
 * returns a Translator which looks up the input
 * character in the map, or just returns the input
 * character if it's not in the map.
 */
new Translator(Map<char,char> characters){
    this(transFunction: char c => characters.get(c) || c);
}

//A translator which provides rot13 ciphering.
//Uses automatic constructor.
var Translator rot13 = new Translator(transFunction:char c => char(int(c) + 13));

//A translator which just changes 's' or 'S' to '
.
//Uses custom constructor.
var Translator sToDollar = new Translator(characters: listToMap([('s', '),('S', ')]));