跳转至

第4章 处理对象

第4章 处理对象

4.1 创建并使用类

可以通过编写构造器来创建一个类的实例。可以将构造器视为对象工厂。主构造器(primary constructor, 非正式地用其指代类)的参数定义了字段,并自动生成了访问器方法。在Scala中,不需要像Java一样需要在类中定义一个与类名相同的方法来当作构造器。

Car made in year 2015
Miles dirven 0
Drive for 10 miles
Miles driven 10

如果类定义没有主体,就没有必要使用大括号({})。

class CreditCard(val number: Int, var creditLimit: Int)

CreditCard带有两个字段、一个构造器、不可变的number的getter以及可变的creditLimit的getter和setter。Scala编译器会自动把上面的代码转化为全面的类。先编译前面的代码,并运行命令javap -private CreditCard查看编译器所生成的代码

Compiled from "CreditCard.scala"
public class CreditCard {
  private final int number;
  private int creditLimit;
  public int number();
  public int creditLimit();  // get方法
  public void creditLimit_$eq(int); // set方法
  public CreditCard(int, int);
}

可以看到编译器自动生成了gettersetter方法以及构造器。但是gettersetter方法并不遵循JavaBean惯例。

Scala会执行主构造器中任意表达式和直接内置在类定义中的可执行语句。

Let's create an instance
Creating an instance of Construct with parameter Sample

除了主构造器,可以使用名为this()的方法定义辅助构造器(auxiliary constructor).

Creating John Smith holds null position
John Smith holds Analyst position
Creating Bill Walker holds null position
Bill Walker holds null position

4.2 遵循JavaBean惯例

Scala编译器默认生成的访问器并不遵循JavaBean的命令规范。如果类要在Java中使用,要在相应的字段声明上标记scala.beans.BeanProperty注解:Scala编译器会准确可靠地生成类似于JavaBean以及Scala风格的getter/setter方法。 javap的输出为

public class Dude {
  private final java.lang.String firstName;
  private final java.lang.String lastName;
  private java.lang.String position;
  public java.lang.String firstName(); // scala风格 get
  public java.lang.String lastName(); // scala风格 get
  public java.lang.String position(); // scala风格 get
  public void position_$eq(java.lang.String); // scala风格 set
  public java.lang.String getFirstName();  // JavaBean风格 get
  public java.lang.String getPosition();   // JavaBean风格 get
  public void setPosition(java.lang.String); // JavaBean风格 set
  public Dude(java.lang.String, java.lang.String);
}

4.3 类型别名

使用type A = Bclass B取上class A的别名。

4.4 扩展一个类

在Scala中使用extends关键字扩展基类。与Java相比,多了两个非常好的限制:其一,方法的重载必须用override关键字;其二,只有主构造器能传递参数给基类的构造器。

Scala重载必须使用override关键字。

ID: 1 Year: 2015 Fuel Level: 100

因为Car中的属性id和year派生自Vehicle,通过在类Car的主构造器相应的参数前加上关键字override表明了这一点。Scala编译器不会为这两个属性生成字段,而是将这些属性的访问器方法路由到基类的相应方法。

4.5 参数化类型

范型或者参数化类型有助于创建能够同时应对多种不同类型的类和函数。类型可以在编译时而不是在代码编写时确定,这样能够使代码更加扩展且类型安全。在Java中,尖括号(<>)被用于指定范型。在Scala中使用防括号([])来替代。

Got hello (class java.lang.String)) there (class java.lang.String)
Got 4 (class java.lang.Integer)) 5 (class java.lang.Integer)
Got 4 (class java.lang.Integer)) 5 (class java.lang.Integer)

创建一个参数化类和创建参数化函数一样简单。和

message content is howdy
true
false
false

参数化类型在实例创建的时候被指定。如果尝试输入不正确的类型,就会接收到一个严格的报错信息。

4.6 单例对象和伴生对象

在Scala中,创建一个单例要使用关键字object而不是class

Creating marker color red
Creating marker color blue
Creating marker color yellow
marker color blue
marker color blue
marker color red
marker color red
Creating marker color green
marker color green

独立对象(stand-alone object)和任何类都没有自动的联系。例如MarkerFactory。关联到类的单例称为伴生对象(companion object)。

  • 伴生对象的名字和对应类的名字一致。
  • 每一类都可以拥有伴生对象
  • 伴生对象和相应的伴生类可以放在同一个文件中

下面使用一个伴生对象对Marker这个例子进行重写。Marker的构造器被声明为private。然而,它的伴生对象可以访问它。因此可以在伴生对象中创建Marker的实例。

Creating marker color red
Creating marker color blue
Creating marker color yellow
marker color blue
marker color blue
marker color red
marker color red
Creating marker color green
marker color green

4.7 创建枚举类

4.8 包对象