Scala类与对象
类简介简介类是对象的蓝图。一旦你定义了类,就可以用关键字new根据类的蓝图创建对象。在类的定义里,可以放置字段和方法,这些被笼统地称为成员。对于字段,不管是val还是var定义的,都是指向对象的变量。对于方法,用def定义,包含了可执行代码。字段保留了对象的状态或数据,而方法使用这些数据执行对象的运算工作。当类被实例化的时候,运行时环境会预留一些内存来保留对象的状态映像——即变量的内容。 示例创建类示例: class SumAccumulator { var sum = 0 } 然后实例化两次: val acc: SumAccumulator = new SumAccumulator val csa: SumAccumulator = new SumAccumulator? 这时内存里对象的状态映像如下: ? 由于字段sum是var类型而非val,所以sum可以被赋予新的Int类型的值:例如 acc.sum = 3 此时映像会变成: ? Scala类的重要特性访问修饰符
为了保证对象的健壮性,可以把类中字段变为私有的(private)以阻止外界直接对它访问。因为私有字段只能被定义成在同一类里的方法访问,所有跟新字段的代码将锁定在类里。要声明字段是私有的,可以把访问修饰符private放在字段的前面。 代码示例: class SumAccumulator { private var sum = 0 }? 使用private修饰后,任何从类外部对sum的访问都将失败 val acc: SumAccumulator = new SumAccumulator acc.sum = 3 //编译不通过,因为sum是私有的 ? 方法1.?Scala方法参数类型 Scala方法参数中的类型都是val而不是var类型 def add(a: Int,b: Int): Int = { a = a + b //编译错误:Reassignment to val (就是对val类型赋值的错误) a }? 2.?Scala方法中return Scala方法中的return可以去掉,从而简化代码 代码示例: def numSum(num:Int):Int ={ val sum = num + 10 sum //省略return关键字,简化代码 } 此时返回值就是sum 3.?Scala方法中的“=”号 Scala方法中的“=”号非常重要:因为Scala编译器可以把任何类型转换为Unit,如果定义的函数中”=”号忘记了添加,那么Scala会默认它返回值为Unit类型。若你本来想返回一个String类型的值,由于没有添加”=”,String类型返回值会被转换为Unit类型而丢弃掉。 代码示例: def printStr() { "这是String类型的返回值" } 返回结果为: ??() 正确代码示例: def printStr(): String = { "这是String类型的返回值" } ?返回结果为: ???这是String类型的返回值 4.?Scala方法表达式 假如某个方法仅计算单个结果的表达式,这可以去掉花括号,如果结果表达式很短,甚至可以把它放在def的同一行里。 代码示例: def numSum(num:Int):Int = num + 10 5.?Scala中分号推断 scala程序中,语句末尾的分号通常是可选的,若一行仅有一个语句也可以不加分号。不过,如果一行包含多条语句时,分号则是必须的。 不加分号: if(x < 2) println("too small") else println("ok") 必须加分号: val x = 10; println(x) //两个语句,必须加分号 Scala通常的风格是把操作符放在行尾而不是行头: 错误示例: val x = 10; val y = 3 val z = x //它会被编译器识别为z = x ; +y 两个语句 +y 打印结果: 正确示例: val x = 10;
val y = 3
val z = x +
y
打印结果: Scala分号推断规则:
6. Scala无参方法 调用Scala类中无参方法时,可以写上括号,也可以不写。对于类中的取值器来说,去掉()是不错的风格。 代码示例: object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.talk() //ok per.talk //同样ok } } class Person { def talk(): Unit = println("Talking") } 如果你想强制使用这种风格,可以在声明方法时不带() 代码示例: per.talk() //此时这种写法是错误的 per.talk //OK的 class Person { def talk = println("Talking") } Scala类中getter和settergetter和setterScala类中使用公有字段的话,任何人都可以修改这个字段,使得安全性大大降低。所以我们更倾向于使用getter和setter方法: Scala对类中每个字段都提供了getter和setter方法,分别叫做age和age_=, 代码示例: 创建一个exam1.scala文件,文件内容如下 class Person { var age = 0 } 如果想查看这些方法,可以先编译Person类,用scalac命令编译,然后用javap查看字节码: D:workworkspacescala>scalac exam1.scala D:workworkspacescala>javap Person Compiled from "exam1.scala" public class Person { public int age(); public void age_$eq(int); public Person(); } D:workworkspacescala> ? ? 你可以自己重新定义getter和setter方法。 代码示例: object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.age = 18 per.age = 16 //由于在setter里面控制了age不能变小,所以执行结果age不会变 println(per.age) per.age = 19 //使用setter,赋予大于原来的age println(per.age) } } class Person { private var privateAge = 0 //变成私有变量并改名 def age = privateAge def age_=(newAge: Int) { // age_= 不能有空格 if (newAge > privateAge) privateAge = newAge //使得年龄不能变小 } } 打印结果为:
18
19
Scala中每个字段生成getter和setter的限制:
Scala在实现类中属性时的四个选择:
Bean属性JavaBean规范把Java属性定义为一堆getFoo和setFoo方法。类似于Java,当你将Scala字段标注为 @BeanProperty时,getter和setter方法会自动生成。 代码示例: import scala.beans.BeanProperty object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person per.name = "zhagnsan" per.setName("lisi") //BeanProperty生成的setName方法 println(per.getName) //BeanProperty生成的getName方法 } } class Person { @BeanProperty var name:String = _ } 打印结果为: Lisi 上述类Person中由@BeanProperty生成了四个方法: name: String name_= (newValue: String): Unit getName(): String setName (newValue: String): Unit 图示为针对字段生成的方法: ? Scala类中构造器和java或C++一样,Scala也可以有任意多的构造器。不过,Scala类有一个构造器比其他所有构造器都更为重要,它就是主构造器。除了主构造器外,Scala类还可以有任意多的辅助构造器。 辅助构造器Scala中辅助构造器和Java或C++十分相似,只有两处不同:
这里有一个带有两个辅助构造器的类。 代码示例: object exam1 { def main(args: Array[String]): Unit = { val per1: Person = new Person //主构造器 val per2: Person = new Person("Bob") //第一个辅助构造器 val per3: Person = new Person("Bob",18) //第二个辅助构造器 } } class Person { 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方法定义,而是与类定义交织在一起。 主构造器的参数直接放在类名之后 代码示例: object exam1 { def main(args: Array[String]): Unit = { val per: Person = new Person("Bob",18) //使用主构造器实例化对象 println(per.name + " : " + per.age) } } class Person(val name: String,val age: Int) { //产生私有的name和age,没有setter //..... } 打印结果: Bob : 18
上述简短的Person类定义极大简化了相同功能的Java代码: 与上例相同功能的Java代码示例: class Person { private String name; private int age; public Person(String name,int age) { this.name = name; this.age = age; } public String name() { return this.name; } public int age() { return this.age; } } ? Scala主构造器定义的所有语句都会执行 代码示例: class Person(val name: String,val age: Int) { //产生私有的name和age,没有setter println("主构造器定义的所有语句都会执行") //每次使用主构造器时都会执行 def description = name + "is" + age + "years old" } 不同类型的主构造器参数对应会生成的字段和方法: ? 如果想让主构造器变成私有的,可以像这样放置private关键字: class Person private (val name: String,val age: Int) { //...... } 这样一来,类用户就必须通过辅助构造器来构造Person对象了. Scala嵌套类在Scala中,你几乎可以在任何语法结构中内嵌任何语法构造。你可以在函数中定义函数,在类中定义类。 代码示例: ?
? ? 考虑有如下两个网络: ?
? 在Scala中,每个实例都有它自己的Member类,就和它们有自己的members字段一样。也就是说chatter.Member和myFace.Member是不同的两个类。 ? 作用:拿网络示例来说,你可以在各自的网络中添加成员,但是不能跨网添加成员。 代码示例: ?
? 在嵌套类中,你可以通过?外部类.this?的方式来访问外部类的this引用,就像Java那样。 ?
? 如果你觉得需要,也可以使用如下语法建立一个指向该引用的别名: ?
? 上述语法使得outer变量指向Network.this。对这个变量,你可以使用任何合法的名称。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |