简单类和无参方法
在Scala中,类并不声明为public。Scala源文件可以包含多个类,所有这些类都具有公有可见性。Scala类最简单的形式看上去和Java或C++的很相似,如
class Counter {
private var value = 0 //你必须初始化字段
def increment(){ value += 1} //方法默认是公有的
def current() = value
}
object Counter {
def main (args: Array[String] ): Unit = {
val myCounter = new Counter() //或new Counter
myCounter.increment()
println(myCounter.current()) //或myCounter.current
}
}
注:上述示例中定义了Counter类和Counter的伴生对象。你可以在伴生对象中通过main方法调用Counter类,调用Counter方法或创建Counter实例时,你可以写圆括号或者不写。一般来说,你调用改值器方法(即改变对象状态的方法)时使用(),调用取值器方法(即不会改变对象状态的方法)时去掉()是不错的风格。
属性
属性:类的属性并非指的是类的成员变量,属性是一个或两个代码块,表示一个get访问器和/或一个set访问器。当读属性时,执行get访问器的代码块;当向属性分配一个新值时,执行set访问器的代码块。不具有set访问器的属性被视为只读属性,不具有get访问器的被视为只写属性,同时具有这两个访问器的属性被视为读写属性。
1.带getter和setter的属性-读写属性
Scala对每个用var修饰的字段都提供getter和setter方法。如,我们定义一个公有字段:class Student {var age = 0;}。在Scala生成面向JVM的类的过程中,编译器会自动生成一个私有的age字段以及相应的getter和setter公有方法。如果我们将age声明为private,则getter和setter方法也是私有的。在Scala中,getter和setter分别为age 和 age_=。如,
class Student {
var age = 0;
}
object Student {
def main(args: Array[String]) {
val stu = new Student
stu.age = 21 //将调用stu.age=(21)
println("age:" + stu.age) //将调用方法stu.age
}
}
注:输出结果为age:21。
重新定义getter和setter方法
class Student {
private var privateAge = 0;
def age = privateAge
def age_=(newValue: Int): Unit ={
if(newValue > privateAge){
privateAge = newValue
}
}
}
object Student {
def main(args: Array[String]) {
val stu = new Student
stu.age = 30
stu.age = 20
println("age:" + stu.age)
}
}
注:getter方法为age,setter方法为age_=。本示例输出结果为30。
2.只带getter的属性
如果你想定义一个只读属性(即有getter方法而没有setter方法的属性),你可以使用val修饰字段。如,class Student {val age = 0;},编译后会生成一个私有的final字段和一个getter方法。但是在Scala中,你不能实现只写属性(即带有setter方法而没有getter方法的属性)。
3.对象私有字段
在Scala中(Java和C++也一样),方法可以访问该类的所有对象的私有字段。如,
class Test {
private var value = 0;
def increment() { value += 1}
def isLess(other: Test) = value < other.value
}
注:因为other是Test对象,所以other可以访问value属性。如果我们通过private[this]修饰字段,则不能通过other访问value属性,因为该字段是当前对象私有的,所以Test类的方法只能访问到当前对象的value字段,而不能访问同样是Test类型的其他对象的该字段。对于对象私有的字段,Scala根本不会生成 getter或setter方法。对于类私有的字段,Scala生成私有的getter和setter方法。
Scala允许你将访问权限赋予指定的类,即通过private[类名]修饰符定义仅有指定类的方法可以访问的字段。这里的类名必须是当前定义的类,或者是包含该类的外部类(在这种情况下,编译器会生成辅助的getter和setter方法,允许外部类访问该字段)。如,
class OuterOne {
class Inner {
private[OuterOne] val test = 0
}
class InnerOne {
val outer = new OuterOne
val inner = new outer.Inner
inner.test
}
def test(): Unit ={
val outer = new OuterOne
val inner = new outer.Inner
inner.test
}
}
class OuterTwo {
def test(): Unit ={
val outer = new OuterOne
val inner = new outer.Inner
}
}
注:Inner类中test字段为指定类访问字段,其指定类为OuterOne,所以OuterOne类和任意的OuterOne类的内部类可访问字段test,其它类无访问权限。若test字段的指定类为OuerTwo,则编译会报错,所以类指定字段指定的最大范围是其外部类。若test字段只有private修饰,则只有Inner类可访问其test字段。若test字段用public修饰,则任意拥有Inner实例的类都可以访问test字段。
如果你想使用和Java中一样的getter和setter方法,你需要使用标注修饰字段即在字段前添加@BeanProperty。如,class Student {@BeanProperty var name : String = _},编译器会生成四个方法:name: String、name_=(newValue: String): Unit、getName(): String和setName(newValue: String):Unit。
6.总结
Scala字段 | 生成的方法 | 使用场景 |
val/var name | 公有的name name_=(仅限于var) | 实现一个可以被公开访问并且背后是以字段形式保存的属性 |
@BeanProperty val/var name | 公有的name getName() name_=(仅限于var) setName(仅限于var) | 与JavaBean互操作 |
private val/var name | 私有的name name_=(仅限于var) | 用于将字段访问限制在本类的方法,就和Java一样。 尽量使用private-除非你真的需要一个公有的属性。 |
private[this] val/var name | 无 | 用于将字段访问限制在同一个对象上调用的方法,使用率低 |
private[类名] val/var name | 依赖于具体实现 | 将访问权限赋予外部类,使用率低 |
辅助构造器
和Java和C++一样,Scala也可以有任意多的构造器。不过,Scala类有一个构造器比其它所有构造器都更为重要,它就是主构造器。除了主构造器以外,类还可以可有任意多的辅助构造器。辅助构造器使用方式:
1.辅助构造器的名称为this。
2.每一个辅助构造器都必须以一个对先前已定义的其它辅助构造器或主构造器的调用开始。
2.每一个辅助构造器都必须以一个对先前已定义的其它辅助构造器或主构造器的调用开始。
如:
class Test {
private var name = "";
private var age = 0;
def this(name: String){
this()
this.name = name
}
def this(name: String, age: Int){
this(name)
this.age =age
}
}
注:注意辅助构造器的使用方式。
主构造器
在Scala中,每个类都有主构造器。主构造器并不以this方式定义,而是与类定义交织在一起。
1.主构造器的参数直接放置在类名之后。如,
class Test(val name: String, val age: Int) {
...
}
注:主构造器和类名保持一致。主构造器的参数被编译成字段,其值被初始化成构造时传入的参数。如果一个类没有显示定义主构造器,则该类会自动拥有一个无参的主构造器。
2.主构造器会执行类定义中的所有语句
1.使用伴生对象
2.使用类型投影
class Test(val name: String, val age: Int) {
println("Test")
def des = "name: " + name + " age: " + age
}
object Test {
def main(args: Array[String]) {
val test = new Test("lm", 26)
}
}
注:println语句是主构造器的一部分,每当有对象被构造出来时,上述代码就会被执行。执行结果为:Test。如果类名之后没有参数,则该类具备一个无参主构造器,这样一个构造器仅仅是简单的执行类体中的所有语句而已。
3.主构造器参数生成的字段和方法
4.私有主构造器
主构造器参数 | 生成的字段/方法 |
name: String |
1.如果有方法使用name,则该字段为不可变的对象私有字段,等同于private[this] val的效果
2.如果没有方法使用name,则没有该字段,它仅仅是一个可以被主构造器中代码访问的普通参数
|
private val/var name: String | 私有字段,私有的getter/setter方法 |
val/var name: String | 私有字段,公有的getter/setter方法 |
@BeanProperty val/var name: String | 私有字段,公有的Scala版和JavaBean版的getter/setter方法 |
4.私有主构造器
如果主构造器用private修饰,则主构造器会变为私有的,在这种情况下你必须通过辅助构造器才能构造对象。如,
class Test private(val name: String, val age: Int) {
def this(name: String){
this(name, 26)
}
def des = "name:" + name + " age:" + age
}
object Test {
def main(args: Array[String]) {
val test = new Test("lm")
println(test.des)
}
}
注:运行结果为name:lm age:26。
嵌套类
在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。你可以在函数中定义函数,在类中定义类。如,
import scala.collection.mutable.ArrayBuffer
class Person {
class Student(val name: String){
val students = new ArrayBuffer[Student]
}
private val persons = new ArrayBuffer[Student]
def join(name: String) = {
val student = new Student(name)
persons += student
student
}
}
object Person {
def main(args: Array[String]) {
val per = new Person
var stu = new Person
val fred = per.join("Fred") //类型为per.Student
val wilma = per.join("Wilma") //类型为per.Student
fred.students += wilma
val barney = stu.join("Barney") //类型为stu.Student
}
}
注:上述示例中Student是Person的内部类。在Scala中,每个实例都有它自己的Student类(与java的区别:Java内部类是属于外部类的,Scala内部类是属于外部对象的),就和它们都有自己的字段一样,所以fred.students +=wilma是正确的,fred.students += barney是错误的。如果你不想有这个效果,你可以用两种方式去解决-即把Student类移至伴生对象中或使用类型投影。
1.使用伴生对象
import scala.collection.mutable.ArrayBuffer
class Person {
private val persons = new ArrayBuffer[Person.Student]
def join(name: String) = {
val student = new Person.Student(name)
persons += student
student
}
}
object Person {
class Student(val name: String){
val students = new ArrayBuffer[Student]
}
def main(args: Array[String]) {
val per = new Person
var stu = new Person
val fred = per.join("Fred")
val wilma = per.join("Wilma")
fred.students += wilma
val barney = stu.join("Barney")
fred.students += barney
}
}
注:变量fred、wilma、barney的类型都为Person.Student。
2.使用类型投影
import scala.collection.mutable.ArrayBuffer
class Person {
class Student(val name: String){
val students = new ArrayBuffer[Person#Student] //含义是-任何Person的Student
}
private val persons = new ArrayBuffer[Student]
def join(name: String) = {
val student = new Student(name)
persons += student
student
}
}
object Person {
def main(args: Array[String]) {
val per = new Person
var stu = new Person
val fred = per.join("Fred")
val wilma = per.join("Wilma")
fred.students += wilma
val barney = stu.join("Barney")
fred.students += barney
}
}
注:变量fred、wilma、barney的类型都为Person#Student。
在Scala的内嵌类中,你可以通过外部类.this的方式来访问外部类的this引用,就像java一样。如,
class Outer(val name: String) { outer =>
class Inner(val name: String) {
def description = name + " inside " + outer.name
}
}
object Outer {
def main(args: Array[String]) {
val outer = new Outer("outer")
val inner = new outer.Inner("inner")
println(inner.description)
}
}
注:上述示例的结果为inner inside outer。outer => 语法使得outer变量指向Outer.this。