1. 单例对象
Scala没有静态方法或静态字段,你可以用Object这个语法结构来达到同样的目的。对象定义了某个类的单个实例,包含了你想要的特性。例如:
object ObjectTest {
def main(args: Array[String]): Unit = {
//如果属性和方法定义在object组件中,就可以直接使用类型名称访问
//和java中的static语法一致
println(ObjectTest11.id)
println(ObjectTest11.m1(3))
//也可以构建对象
val obj_1 = ObjectTest11
val obj_2 = ObjectTest11
println(obj_1 == obj_2)//true
println(obj_1 eq obj_2)//true
println(obj_1 ne obj_2)//false
println(obj_1.equals(obj_2))//true
val obj22:ObjectTest22= new ObjectTest22()
println(obj22.id)
println(obj22.m1(3))
val obj33_1 = new ObjectTest33("xh",18)
val obj33_2 = new ObjectTest33("xh", age = 18)
println(obj33_1 == obj33_2)//true 默认是调用equals方法
println(obj33_1 eq obj33_2)//false 比较引用地址
println(obj33_1 ne obj33_2)//true 比较引用地址
println(obj33_1.equals( obj33_2))//true 比较toString方法的值 必须要重写类的toString方法和equals方法,否则还是比较对象的引用地址
}
}
// object修饰的是单例对象
object ObjectTest11 {
val id:Int = 11
def m1(x:Int) = x+1
}
class ObjectTest22 {
val id:Int = 11
def m1(x:Int) = x+1
}
class ObjectTest33(name:String, age:Int) {
def m1(x:Int) = x+1
override def toString: String = name +"-" + age
override def equals(obj: Any): Boolean = this.toString.equals(obj.toString)
}
对象本质上可以拥有类的所有特性—它甚至可以扩展其他类或特质,只有一个例外:不能提供构造器参数。
2. 伴生对象
在Java或C++中,通常会用到即有实例方法又有静态发给发的类。在Scala中,你可以通过类和与类同名的“伴生”对象来达到同样的目的。例如:
class Account {
val id = Account.newUniqueNumver()
private var balance = 0.0
def deposit(amount: Double) = {
balance += amount
balance
}
}
object Account { // 伴生对象
private var lastNumber = 0
private def newUniqueNumver() = {
lastNumber += 1
lastNumber
}
}
类和它的伴生对象可以相互访问私有特性。它们必须存在于同一个源文件中。
3. apply类
我们通常会定义和使用对象的apply方法。当遇到如下形式的表达式时,apply方法就会被调用:
Object(参数1, …, 参数N)
通常,这样一个apply方法返回的是伴生类的对象。
例如,Array对象定义了apply方法,让我们可以用下面这样的表达式来创建数组:
Array(“Mary”, “had”, “a”, “little”, “lamb”)
为什么不用构造器呢?对于嵌套表达式而言,省去new关键字会方便很多,例如:
Array(Array(1, 7), Array(2, 9))
注意:
Array(100)和new Array(100)很容易搞混。前一个表达式调用的是apply(100), 输出一个单元素(整数100)的Array[Int]; 而第二个表达式调用的是构造器this(100),结果是Array[Nothing], 包含了100个null元素。
apply方法的示例:
class Account private (val id: Int, initialBalance: Double) {
private var balance = initialBalance
…
}object Account { // 伴生对象
def apply(initialBalance: Double) =
new Account(newUniqueNumber(), initialBalance)
…
}
这样一来你就可以用如下代码来构造账号了:
val acct = Account(1000.0)
4. 应用程序对象
每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String]=>Unit:
object Hello{
def main(args: Array[String]): Unit = {
println("Hello, World!")
}
}
除了每次都提供自己的main方法外,你亦可以扩展App特质,然后将程序代码放入构造器方法体内:
object Hello extends App {
println("Hello, World!")
}
如果需要命令行参数,则可以通过args属性得到:
object Hello extends App {
if (args.length > 0)
println("Hello, " + args(0))
else
println("Hello, World!")
}
如果调用该程序时设置了scala.time选项的话,程序退出时会显示逝去的时间。
$ scalac Hello.scala
$ scala -Dscala.time Hello Fred
Hello, Fred
[total 4ms]
所有这些,都是来源于App特质扩展另一个特质DelayedInit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedInit方法中。App特质的main方法捕获到命令行参数,调用delayedInit方法,并且还可以根据要求打印出逝去的时间。
5. 枚举
和Java或C++不同,Scala并没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举类。
每次条用Value 对象都返回内部类的新实例,该内部类也叫做Value.
或者也可以向Value出入Id、名称,或者两个参数都传,下面是不传参数:
object TrafficLightColor extends Enumeration{
val Red, Yellow, Green = Value
// 是如下代码的简写
//val Red = Value
//val Yellow = Value
//val Green = Value
def main(args: Array[String]): Unit = {
println(Red) // 还是字段名本身
println(Red.id) // 0 id从0开始,后面依次加1
}
}
传参数:
object TrafficLightColor extends Enumeration {
// 是如下代码的简写
val Red = Value(0, "stop")
val Yellow = Value(10) // id为10,名称为Yellow
val Green = Value("go") // id为11(在前一个基础上增1)名称为go
def main(args: Array[String]): Unit = {
println(Red) // stop
println(Red.id) // 0 id从0开始
println(Green) // go
println(Green.id) // 11
}
}
object TrafficLightColor extends Enumeration {
val Red = Value(0, "stop")
val Yellow = Value(10) // id为10,名称为Yellow
val Green = Value("go") // id为11(在前一个基础上增1)名称为go
def main(args: Array[String]): Unit = {
println(TrafficLightColor.Red) // 其他对象调用
for (c <- TrafficLightColor.values) println(c.id + ": " + c) // 输出所有枚举id和值
}
}
6. 样例类
会默认将equals和toString方法实现,不用我们亲自实现
object ObjectTest {
def main(args: Array[String]): Unit = {
//普通类
val obj_1 = new ObjectTest11("xh",18)
val obj_2 = new ObjectTest11("xh", 18)
println(obj_1 == obj_2)//false
println(obj_1 eq obj_2)//false
println(obj_1 ne obj_2)//true
println(obj_1.equals(obj_2))//false
println("---------------------------")
//样例类
val obj_11 = new ObjectTest22("xh",18)
val obj_22 = new ObjectTest22("xh", 18)
println(obj_11 == obj_22)//true
println(obj_11 eq obj_22)//false
println(obj_11 ne obj_22)//true
println(obj_11.equals(obj_22))//true
}
}
// object修饰的是单例对象
class ObjectTest11(name:String, age:Int) {
def m1(x:Int) = x+1
}
case class ObjectTest22(name:String, age:Int) {
def m1(x:Int) = x+1
}
总结:
- 自定义类,如果不是样例类,需要重写toString方法和equals方法,这样下面的比较就会和样例类实现相同的结果
a. ==和equals方法比较toString方法所得的结果
b. eq(是否相等)比较对象的引用地址
c. ne(不相等吗)比较对象的引用地址 - 如果自定义类不是样例类,也没重写toString和equals方法,这样上面的比较方法都是比较对象的引用地址了
- 单例对象类似Java中的类的静态方法,直接使用类名称调用属性方法,又因是单例对象,所以四个比较方法除ne外都是相等的
样例类
1,构造器中的参数如果不被声明为var的话,它默认是val类型的,一般推荐构造器中的参数声明为var
2,自动创建伴生对象,同时在里面给我们实现apply方法,使使用的时候可以不使用new关键字创建对象
3,伴生对象中同样会帮助我们是想unapply方法,从而使case class应用于模式匹配
4,实现自家的toString、hashCode、copy、equals方法
除此之外,case class 与scala中其他普通类没区别