Scala之类(getter与setter属性,主构造器和辅助构造器)

scala中的类的特点
1.类中字段自动实现getter和setter方法
2.可以实现自定义getter和setter方法
3.使用@BeanProperty注解生成getXXX和setXXX方法
4.每个类有一个主构造器,随类定义一起声明,参数可以直接成为类字段,主构造器执行类体中的所有语句。
5.可以拥有任意多的辅助构造器,叫做this。

1.简单类与无参方法

class Person {  //Person类
  private var age = 0 //定义私有属性
  def increment(){age += 1}  //定义increment方法,方法默认是公有的
  def current = age //定义current方法
  def act(person:Person): Unit ={  //定义act方法
  
}

scala中,类不声明为public,源文件可以包含多个类,所有这些类都具有公有可见性
使用类只需构造对象然后调用方法即可

  val person = new Person() //创建Person实例
    person.increment()  //调用person对象的increment方法
    person.increment
    println(person.current)

调用无参方法比如increment时,可以加(),也可以不加。
何时应该使用哪种方法?对于改值器方法(即改变对象状态的方法)使用(),而对于取值器方法(不会改变对象状态的方法)可以丢掉()

  person.increment()  //对改值器使用()
  println(person.increment)  //对取值器不使用()

可以通过以不带()方式声明increment方法来强制这种风格。如上面的

class Person{
...
def current = age //定义中就不带()
}

这样一来类的使用者就必须使用person.current而不带()

2.自动带有getter和setter属性
在java中,我们喜欢:

public class Person {
	private int age;
    public int getAge() {return age;}
    public void setAge(int age) {this.age = age;}
}

像这样一对getter/setter通常被称为属性property,我们说Person类有一个age属性。但这样好在哪里呢,与被声明为公有字段,现在任何人都可以调用set或者get方法对其进行改变,但之所以说使用getter和setter方法,是因为它们让你可以从简单的get、set机制出发,在需要的时候进行改进。
而scala对每个字段都提供了getter和setter方法,现在如有:

class Person {
	var age  = 0
}

scala生成面向JVM的类,其中有一个私有的age字段以及相应getter和setter方法,这两个方法是公有的,因为我们没有将age声明为private(对于私有字段而言,getter和setter方法也是私有的)
在scala中,getter和setter分别叫做age和age_ 。如

println(fred.age)  //将调用方法fred.age()
fred.age = 20  //将调用fred.age = (21)

如果想亲眼看到这两个方法,可以编译Person类,然后javap查看字节码文件
scalac Person.scala
javap -private Person
可以看见编译器将创建age和age_ e q = eq方法,(=被翻译成 eq,因为JVM中不允许方法名中出现=)。在scala中,getter和setter方法并非被命名为了getXxx和setXxx的形式,但含义都是一样的。
任何时候我们都可以重新定义getter和setter方法,如:

class Person{
	private var privateAge = 0 //私有声明age
	def age = privateAge
	def age_ = (newValue:Int){
		if(newValue > privateAge) privateAge = newValue  //岁月无情,岁数只能增加
	}
}

现在Person类的使用者仍然可以访问fred.age,但却没法使fred变年轻了

val fred = new Person
fred.age = 30
fred.age = 20
println(fred.age)

scala对每个字段生成getter和setter方法听着恐怖,但我们可以控制这个过程
1.如果字段是私有的,那么getter和setter方法也是私有的
2.如果字段是val,则只有getter方法生成
3.如果不需要任何getter和setter方法,可以将字段声明为private[this](见下)

3.只带getter属性
有时候我们只需要一个只读属性,有getter但没有setter属性,如果属性的值在对象构建完成之后就不在改变,则使用val进行字段声明

class Message {
  val timpStamp = new java.util.Date
...
}

scala会生成一个私有的final字段和一个getter方法,但没有setter。
不过,有时我们需要这样一个属性:客户端不能随便改值,但可以通过其他方式改变,那么文章最开始的Person类就是一个例子,Person类有一个current属性,当increment方法被调用时更新,但并没有对应的setter。
但我们并不能通过val来实现这样一个属性——val永不改变。你需要提供一个私有字段和一个属性的getter方法,如:

class Counter {
  private var value = 0
  def increment() = {value += 1}
  def current = value //声明中没有()
}
val  n = myCounter.current

注意getter方法定义中没有(),所以不需要带()来调用。
总结一下,在实现属性时你有下面四个选择:
1.var foo: scala自动合成一个getter和一个setter。
2.val foo: scala自动合成一个getter。
3.由你来定义foo和foo_=方法
3.由你来定义foo方法
在scala中,不能实现只写属性,即带有setter而没有getter的属性。
当在scala类中看到字段的时候,记住它和Java,C++中的字段不一样,它是一个私有字段,加上getter方法(对val字段而言)或者getter方法和setter方法(对var而言)

4.对象私有字段
在scala中,方法可以访问该类的所有对象的私有字段,如:

class Counter {
  private var value = 0
  def increment() {value += 1}
  def isLess(other:Counter) = value < other.value  //可以访问另一个对象的私有字段
}

之所以可以访问是因为other也是Counter对象。
scala允许我们定义更加严格的访问限制,通过private[this] 修饰符实现

private[this] var value = 0 //这样类似某个对象.value这样的访问将不被允许

现在,Counter类的方法只能访问到当前对象的value字段,而不能访问同样是Counter类型的其他对象的该字段。这样的访问有时被称为对象私有的。对于类私有的字段,scala生成私有的getter和setter方法,但对于对象私有的字段,scala根本不会生成getter和setter方法。
scala还允许将访问权赋予指定的类,private[类名]即可定义仅有指定类的方法可以访问给定的字段,这里的类名必须是当前定义的类,或者是包含该类的外部类,在这种情况下,编译器会生成辅助的getter和setter方法,允许外部类访问该字段,这些类将会是公有的。

5.Bean属性
scala对于定义的字段提供了getter和setter方法,但这并不是java所期望的,JavaBean规范把java属性定义为一对getFoo和setFoo方法,许多java工具都依赖这样的命名方式。
当你将scala字段标注为@BeanProperty时,这样的方法会自动合成,如:

import scala.beans.BeanProperty
class Person {
  @BeanProperty var name:String = _
}

那么将会生成四个方法:
1.name:String
2.name_=(newValue:String):Unit
3.getName():String
4.setName(newValue:String):Unit
如果在主构造器中定义了某字段,并且需要JavaBean版本的getter和setter方法,这样即可:

class Person(@BeanProperty var name: String)

在这里插入图片描述

6.辅助构造器
scala可以有任意多的辅助构造器。但scala类的主构造器是最重要的。
1.辅助构造器名称为this,在Java中,构造器名称和类名相同。
2.每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始
这是带有两个辅助构造器的类:

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
  }
}

现在我们可以以三种方式创建对象:

val p1 = new Person //主构造器
val p2 = new Person("Fred") //第一个辅助构造器
val p3 = new Person("Fred",20) //第二个辅助构造器

7.主构造器
scala中每个类都有主构造器。主构造器不以this命名,而是与类定义交织在一起
如:

class Person(val name: String,val age:: Int) {
...
  }

主构造器的参数被编译城字段,值被初始化城构造时传入的参数,上例中name和age成为Person类的字段,如new Person(“Fred”,33)这样构造对象
想一想java的类定义和构造器,scala只用了半行就实现了

public 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;}
  ...
}

主构造器会执行类定义中的所有语句,如:

class Person(val name: String,val age: Int) {
  println("primary constructor")
  def description = name + "is" + age + "years old"
}

println语句是主构造器的一部分,每当有对象被构造时,这些语句就会被执行。
当我们需要在构造过程中配置某个字段时这个特性很有用。如:

class MyPro {
  private val props = new Properties
  props.load(new FileReader("myprog.properties"))
}

注:如果类名后没有参数,那么该类具有一个无参主构造器,这样的构造器只是执行类中的所有语句罢了。

我们可以在主构造器中使用默认参数来避免过多使用辅助构造器。如:

class Person(val name: String = "",val age: Int = 0)

主构造器参数可以是上述表格中的任意形态。如:

class Person(val name: String,private var age: Int)

构造参数也可以是普通的方法参数,不带val或者var,如何设置取决他们在类中如何被使用。
如果不带val或var的参数至少被一个方法所用了,它将会被提升为字段,如:

class Person(name: String,age: Int) {
  def description = name + " is " + age + "years old"
}

上面代码声明并初始化了不可变字段name和age,而这两个字段都是对象私有的,等同于private[this] val 的效果。否则,该参数不被保存为字段,仅为可以被主构造器中的代码访问的普通参数。
在这里插入图片描述
可以这么理解主构造器:在scala中,类也接受参数,就像方法一样。
如果想让主构造器变为私有可以这样:

class Person private(val name: String){...}

这样类用户只能通过辅助构造器来构造person对象了。

8.嵌套类
scala中,我们可以在任何语法结构中嵌套任何语法结构,如可以在函数中定义函数,类中定义类,如:

class Network {
  class Member(val name: String){
    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类,就和它们有自己的members字段一样,也就是说,chatter.Member和myFace.Member是不同的两个类。
这和Java不同,在java中内部类从属于外部类。
拿网络类举例,我们可以在各自网络内添加成员,但不能跨网络对象添加成员

val fred = chatter.join("Fred")
val wlima = chatter.join("Wlima")
fred.contacts +=wlima //OK
val barey = myFace.join("Barey")  //类型为myFace.Member
fred.contacts += barey //不可以这样做,不能将一个myFace.Member添加到chatter.Member元素缓冲中

如果希望实现这样的效果,可以有两种解决方法:
可以将Member类移到别处,一个不错的位置是Network的伴生对象

object Network {
    class Member(val name: String) {
       val contacts = new ArrayBuffer[Member]
	}
}

class Network {
	private val members = new ArrayBuffer[Network.Member]
}

或者可以使用类型投影Network#Member,含义是任何Network的Member,如:

class Network {
	class Member (val name: String) {
		val contacts = new ArrayBuffer[Network#Member]
	}
	...
}

内嵌类中,我们可以通过外部类.this的方式来访问外部类的this引用,就像java那样,也可以用如下语法建立一个指向该引用的别名。

class Network(val name: String){ outer =>
	class Member(val name: String) {
		...
}
}
发布了14 篇原创文章 · 获赞 1 · 访问量 684

猜你喜欢

转载自blog.csdn.net/qq_33891419/article/details/103603922