和java类似,scala也有类,而且概念相同不再赘述。
本章要点
- 定义类
- getter和setter
- 对象私有属性
- Bean属性
- 构造器
- 嵌套类
9.1 类的定义
scala简单类
class Counter {
private var value: Int = 0;//你必须初始化字段
def increment() {//方法默认是公有的
value += 1
}
def current(): Int = {
value
}
}
在Scala中,类并不声明为public。Scala源文件可以包含多个类,所有这些类都具有公有可见性。使用该类需要做的就是构造对象并按照通常的方式来调用方法:
val myCounter=new Counter // 或new Counter()
myCounter.increment()//counter.increment;也可
println (myCounter.current) // counter.current()也可,结果为1
注意:调用无参方法比如current时,你可以写上圆括号,也可以不写,应该用哪一种形式呢,通常认为对于改值器方法,即改变对象状态的方法使用(),而对于取值器方法不会改变对象状态的方法去掉()是不错的风格。
可以通过不带()声明current的方式来强制这种风格:
def current: Int = {
value
}
9.2 scala类的getter、setter
Scala对每个字端都提供getter和setter方法。在这里,我们定义一个公有字段:
class Person{
var name="zhaosc";
}
Scala生成面向JVM的类,其中有一个name字段以及相应的getter方法和setter方法,这两个方法是公有(public)的,因为我们没有将name声明为private;而对私有字段而言,getter和setter方法也是私有的。
可以编译Person类,然后用javap查看字节码,如下:
![](https://img.haomeiwen.com/i11017946/7d5436cad9ad32b5.png)
可以看到,编译器创建了name和name_$eq方法。=号被翻译成$eq,是因为JVM不允许在方法名中出现=,同时默认给出了一个无参的构造方法。
调用如下
var person=new Person
person.name="zsc"//setter
print(person.name)//getter
说明:在Scala中,getter和setter方法并非被命名为getXxx和setXxx,不过它们的用意是相同的。后面会介绍如何生成Java风格的getXxx和setXxx方法,以使得你的Scala类可以与Java工具实现互操作。
重新定义getter、setter方法
为了便于理解scala的getter、setter此版本编写的,有点类似于java风格,不太建议,如下:
class Person{
private var name="";
def getName:String={
this.name
}
def setName(name:String){
this.name=name;
}
}
调用
var person=new Person
person.setName("赵世超")
println(person.getName)
scala风格版本:
class Person{
private var nameStr="";
def name:String={
this.nameStr
}
def name_=(name:String){
this.nameStr=name;
}
}
调用
var person=new Person
person.name_=("赵世超")
println(person.name)
注意:上文提到过Scala对每个字段生成getter和setter方法,不过你可以控制这个过程如下:
- 如果字段是私有的,则getter和setter方法也是私有的
- 如果字段是val,则只有getter方法被生成(val声明的是常量,常量一旦赋值,无法更改,所以无setter)
例如:
class Person{
private val nameStr="zhaosc";
}
它的编译信息如下:
![](https://img.haomeiwen.com/i11017946/22c27c6949511f44.png)
可见,只有getter没有setter;并且val字段,自动加final。
- 如果你不需要任何getter或setter,可以将字段声明为private[this](下一小节讲解)
总结:
1) var 变量: Scala自动合成一个getter和一个setter
2) val 常量: Scala自动合成一个getter
3) 自己定义变量(如:var name)的getter和setter (如:name和name_= 的方法)
4) 自己定义getter(如:name方法)方法
但在Scala中,你不能实现只写属性,即只有setter但没有getter的属性。当你在Scala类中看到字段的时候,记住它和Java或c++中的字段不同。它是一个私有字段,加上getter方法(对val字段而言)或者getter和setter了法(对var字段而言),总之scala类中的字段必须有getter。
9.3 对象的私有属性
我们通常所说的属性一般都是类(class)的属性,私有属性也是类的私有属性,和java不同的是scala竟然有对象的私有属性。
声明方式是通过 private[this],对于对象的私有属性,Scala是不会生成getter或setter方法的。
例如:
class Person {
private var name = "zhaosc";
private[this] var default_salary = 100; //private[this]
private var salary = default_salary;
def raise(value: Int) {
this.salary = this.default_salary + value;// default_salary 只能这么访问,其他方式报错
}
def info(p: Person) {
print(p.name + p.salary)
}
}
编译信息如下:
![](https://img.haomeiwen.com/i11017946/23df24e3cc8eff0d.png)
可见对于对象的私有属性,Scala是不会生成getter或setter方法的。
另外:
Scala还允许将访问权赋予指定的类,private[类名] 修饰符可以定义仅有指定类的方法可以访问给定的字段。这里的类名必须是当前定义的类,或者是包含该类的外部类。在这种情况下,编译器会生成辅助的getter相setter方法,允许外部类访问该字段。这些类将会是公有的,因为JVM并没有更细粒度的访问控制系统,并且它们的名称也会随着JVM实现不同而不同。
用法和private[this]类似不在举例
9.4 Bean属性
scala虽然默认会为字段生成getter和setter方法,但是生成的方法名和我们的编程习惯却大不相同。
此时,可以通过@BeanProperty注解,这样的方法会自动生成,以实现和java代码风格一致
package com.study.scala
import scala.beans.BeanProperty
class Person {
@BeanProperty
var name:String=_;
}
object Person{
def main(args: Array[String]): Unit = {
var person=new Person;
person.setName("zhaosc")
print(person.getName())
}
}
9.5 构造器
scala的构造器分为:主构造器和辅助构造器。
1、主构造器
每个类都有一个主构造器,scala的主构造器并不是以this来定义,而是和类的定义交织在一起。
如下:
package com.study.scala
import scala.beans.BeanProperty
class Person (var name:String,var age:Int){
}
object Person{
def main(args: Array[String]): Unit = {
val person=new Person("zhaosc",18);
print("name:"+person.name+" age:"+person.age)
}
}
当然,也可以通过注解@BeanProperty,添加符合javabean规范的getter和stter方法。
package com.study.scala
import scala.beans.BeanProperty
class Person (@BeanProperty var name:String,@BeanProperty var age:Int){
}
object Person{
def main(args: Array[String]): Unit = {
val person=new Person("zhaosc",18);
print("name:"+person.getName()+" age:"+person.getAge())
}
}
还可以通过在主构造器中使用默认参数来避免过多地使用辅助构造器。例如:
class Person (val name:String="",val age: Int =0 ){
}
TIPS:
- 构造参数也可以是普通的方法参数,不带val或var,这样的参数如何处理取决于它们在类中如何被使用。如果不带val或var的参数至少被一个方法所使用,它将被升格为字段。例如:
class Person(name: String, age: Int) {
def description=name+"is"+age+"years old"
}
上述代码声明并初始化了不可变字段name和age,而这两个字段都是对象私有的。类似这样的字段等同于private[this] val字段的效果。否则,该参数将不被保存为字段。它仅仅是一个可以被主构造器中的代码访问的普通参数。严格地说,这是一个具体实现相关的优化。
- 如果定义类的时候,类名之后没有参数,则系统会默认提供一个无参构造。
2、辅助构造器
scala除了主构造器还有辅助构造器,一个类可以有任意多的辅助构造器。
- 辅助构造器的名称为this。在Java或C++中,构造器的名称和类名相同,当你修改类名时就不那么方便了
- 每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始
例子,如下:
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
}
}
9.6 嵌套类
在Scala中,你几乎可以在任何语法结构中内嵌任何语法结构。你可以在函数中定义函数,在类中定义类。
例子如下:
import scala.collection.mutable.ArrayBuffer
class Network {
class Member(val name: String) {//Member嵌套类
val contacts = new ArrayBuffer[Member]
}
private val members=new ArrayBuffer[Member]
def join(name: String) ={
val m=new Member(name)
members+=m
m
}
}
val chatter = new Network
val myFace = new Network
在Scala中,每个实例都有它自己的Member类,也就是说,chatter.Member和myFace.Member是不同的两个类。
说明:
这和Java不同,在Java中内部类从属于外部类。
Scala采用的方式更符合常规,举例来说,要构建一个新的内部对象,你只需要简单的new这个类名:new chatter.Member。而在Java中,你需要使用一个特殊语法:chatter.new Member()。
Scala内嵌类访问
在内嵌类中,你可以通过外部类.this的方式来访问外部类的this引用,就像Java那样。如果你觉得需要,也可以用如下语法建立一个指向该引用的别名:
class Network(val name: String) { outer =>
class Member(val name: String) {
def dascription = "outside:" + outer.name+" inside:"+ name
}
}
object Network {
def main(args: Array[String]): Unit = {
val network = new Network("wuxian");
val member=new network.Member("luyou")
print(member.dascription)
}
}
class Network { outer=>语法使得outer变量指向Network.this。对这个变量,你可以用任何合法的名称。
网友评论