前言
在《大数据之脚踏实地学19--Scala中类的使用》中我们详细介绍了Scala中类的使用,通过类的构造,借助于new关键词创建不同的实例化对象,进而实现代码的可复用性。在本期我们将继续介绍跟类相关的另外两个主题,即构造器和单例对象。
构造器
通过上一期Scala类的介绍,我们会发现一个非常显著的特征,即给类实例化过程中,所传递的参数都是相同类型或相同个数。那么问题来了,有没有这样一种类,允许用户创建不同类型(即包含不同参数列表或参数类型)的实例化对象?答案是肯定有的。这种类就是我们接下来要介绍的构造器,构造器的另一个用途是可以方便地设定类的初始属性值。
首先,我们来看一下,Scala中构造器的语法结构:
代码语言:javascript复制// 构造器的语法
class ClassName[(Parameters List)] { // 主构造器
def this[(Parameters List)] { 第一个辅助构造器
this[(Parameters List)] // 调用主构造器
... // 辅助构造器的其他属性和方法
}
def this[(Parameters List)] { 第二个辅助构造器
this[(Parameters List)] // 调用主构造器或辅助构造器
... // 辅助构造器的其他属性和方法
}
}
针对如上的构造器语法,有几点内容需要说明:
- 构造器可分为主构造器和辅助构造器;
- 主构造器中除了方法之外的所有代码都会被执行;
- 辅助构造器的创建一定是以this关键词开头;
- 辅助构造器是没有返回值的;
接下来通过两个具体的例子,解释Scala中构造器的使用。如下代码只构造了含义参数列表的主构造器。其中参数使用了三种修饰方法,即val、var和非val非var。
代码语言:javascript复制// 创建一个主构造器
class Friend(val name: String, var age: Int, education: String) {
println("这就是一个最简单的主构造器")
}
// 创建一个对象,指定代码执行的主入口
object ConsDemo1 {
def main(args: Array[String]): Unit = {
// 创建类的实例对象
val friend1 = new Friend("张三",28,"硕士")
// val修饰的name变量可以访问,具有getter权限
println("朋友的名字为:" friend1.name)
// friend1.name = "李四" 将会报错,因为val修饰的变量具有不可变性,不可以设置新值,无setter权限
// var修饰的age变量可以访问,具有getter权限
println("他的年龄为:" friend1.age)
friend1.age = 1
// var修饰的age变量具有可变性,可以设置新值,有setter权限
println("他第二年的年龄为:" friend1.age)
// println(friend1.education) 将会报错,当变量没有val或者var修饰是,该变量既不可以访问也不可以设置新值
// 如果使education字段具有getter权限和setter权限,可以在类内部将该变量重新赋值给新变量
println(friend1)
}
}
结果输出
通过如上代码可至,三种修饰方式的参数,各有不同的功能。val参数仅有getter权限,即理解为读权限;var参数包含getter权限和setter权限,即理解为可读可写权限;非val非var参数是没有读写权限的。
在下面例子中,我们给类创建了主构造器和辅助构造器,其中辅助构造器的功能是计算税后收入。从代码的最后一部分可以看出,我们创建的两个实例化对象p2和p3时,给类Friend2传递了不同个数的参数。p2实际上调用的是类的主构造器,p3则调用的是类的辅助构造器。
代码语言:javascript复制// 创建包含主构造器和辅助构造器的类
class Friend2(val name: String, var age: Int, education: String) {
var income: Int = _ // 变量初始化
// 通过重新赋值,用户可以对Education变量进行访问和设置
var Education = education
println("这就是一个最简单的主构造器")
// 创建辅助构造器
def this(name: String, age: Int, education: String,income:Int){
this(name,age,education) // 调用主构造器
this.income = income // 参数赋值
var res=0.0
if (income <= 5000) {res = income - 0}
else if (income <= 17000) {res = (income - (210 (income-8000)*0.1))}
else {res = (income - (210 900 (income-17000)*0.2))}
println(s"${name}的收入为${income},经过计算,他的税后收入为${res}")
}
}
// 创建一个对象,指定代码执行的主入口
object ConsDemo2 {
def main(args: Array[String]): Unit = {
// 对Friend2类实例化
val p2 = new Friend2("张三",28,"硕士")
// 修改属性的值
p2.Education = "本科" // 修改学历
println(f"${p2.name}的年龄为${p2.age},他的学历为${p2.Education}")
// 对Friend2类实例化
val p3 = new Friend2("张三",28,"硕士", 16500)
}
}
结果输出
单例对象
Scala中是没有静态属性和静态方法的(即没有类似于Java中的voild修饰符),如果需要构造静态属性或静态方法,可以使用object关键词实现该功能,即在Scala中创建单例对象。在作者看来,Scala中单例对象的最大好处是实例化对象时可以不用new关键词。
单例对象可以分为孤立对象和伴生对象两种,孤立对象是指在一个Scala脚本文件中,对象的名称与类的名称不一致,这样的对象是不能访问类中的私有属性或方法的;伴生对象则是指脚本文件中,对象的名称与类的名称完全一致,此时对象和类之间是可以互相访问对方的私有属性和方法的。
接下来,我们通过一个简单的例子加以说明,代码如下:
代码语言:javascript复制// 创建一个与对象Person同名的半生类
class Person(name:String){
println(s"${name}的编号为${Person.newId}.")
// Person.newId就是直接调用了伴生对象中的newId方法
}
// 创建一个与类Person同名的伴生对象
object Person{
// 定义一个私有变量
private var lastid = 0
// 定义一个私有方法
private def newId: Int ={
lastid = 1
lastid
}
}
// 创建一个对象,指定代码执行的主入口
object ObjectDemo {
def main(args: Array[String]): Unit = {
// 类的实例化
val person1 = new Person("张三")
val person2 = new Person("李四")
}
}
结果输出
结语
本期的内容就介绍到这里,下一期我们将介绍Scala中类的继承。如果你有任何问题,欢迎在公众号的留言区域表达你的疑问。同时,也欢迎各位朋友继续转发与分享文中的内容,让更多的人学习和进步。