Scala快跑系列【面向对象入门】

Scala中的类

在本节中,你将会学习如何用Scala实现类。如果你了解Java中的类,你不会觉得这有多难,并且你会很享受Scala更加精简的表示法带来的便利。

本节的要点包括:

1. 类中的字段自动带有getter方法和setter方法
2. 你可以用定制的getter/setter方法替换掉字段的定义,而不必修改使用类的客户端,这就是所谓的"统一访问原则"
3. 用@BeanProperty注解来生成JavaBeans的getXxx/setXxx()方法(大家比较熟悉的一种编程方式)
4. 每个类都有一个主要的构造器,这个构造器和类定义"交织"在一起。它的参数直接成为类的字段。主构造器执行类体中所有的语句
5. 辅助构造器是可选的,它们叫做this
6. 嵌套类

一、类中的字段自动带有getter方法和setter方法

1、简单类和无参方法

class PersonOps {
  private var age = 0 //你必须初始化一个字段
  def increment() {age += 1} //方法默认是公有的
  def currentAge() = age
}

1)在Scala中,类并不声明为public。Scala源文件可以包含多个类,所有这些类都具有公有可见性。

2)如何进行调用?

object MainOps{
  def main(args: Array[String]) {
    val personOps = new PersonOps()
    personOps.increment()
    println("currentAge=> " + personOps.currentAge())
  }
}

3)不能用类直接调用age,因为age是私有字段,只能在PersonOps中访问到。

2)无参方法:

println("currentAge=> " + personOps.currentAge())
println("currentAge=> " + personOps.currentAge)

建议:对设置值的时候,即改变对象状态值或者状态时使用(),而对于取值来说(不会改变对象的状态和值)去掉()是个不错的风格,这也是我们事例中的方法。

personOps.increment()
println("currentAge=> " + personOps.currentAge)

3)你可以通过以不带()的方式声明currentAge来强制这种风格:

class PersonOps {
  private var age = 0 //你必须初始化一个字段
  def increment() {age += 1} //方法默认是公有的
  def currentAge = age
}

结果:这样一来在调用时,必须要用不带()的进行方法调用 println(“currentAge=> ” +
personOps.currentAge)

4)带getter和setter的属性

Java中属性定义

public class PersonAge{ //这是Java
    private int age;
    public int getAge() { return age; }
    public void setAge(int age) { this.age=age; }
}

getter和setter方式比共有字段(public)好在什么地方呢?
之所以说getter和setter方法比公有字段更好,是因为它们让你可以从简单的get/set机制出发,并在需要的时候做改进。

5)scala中的getter和setter方法

class PersonA{
var age = 0
}

Scala生成面向JVM的类,其中有一个私有的age字段以及相应的getter方法和setter方法。这两个方法是公有的,因为我们没有将age声明为private。而对私有字段而言,getter和setter方法也是私有的。
字段默认,调用默认set/get方法,如果字段私有,那么默认的getter/setter方法也将会是私有的,无法调用,但是我们可以自定义覆盖掉默认的

1、在任何时候你都可以自己重新定义getter和setter方法。例如:
/**
  * 自定义getter和setter方式实现
  */
class CustPerson{
  private var privateAge =0 // 变成私有并改名
  def age = privateAge
  def age_= (newValue: Int) {
    if (newValue > privateAge)
      privateAge=newValue // 不能变年轻
    }
}
注意事项:在自定义setter方法时,等号左边的方法名称和=之间不应该有空格,负责会出现变量未定义的错误。
调用:
val custPerson = new CustPerson
custPerson.age  = 30
custPerson.age = 21
custPerson.age_=(50)
println("custPersonAge=> " + custPerson.age)
class SanMaoScala {
  var name: String ="";
  def getName(): String ={
    name
  }
}
object SanMaoOps{
  def main(args: Array[String]): Unit ={
    var sm = new SanMaoScala;
    sm.name_=("sanmao")
    println(sm.name)
  }
}

字段私有,自己编写set/get方法

class SanMaoScala {
  private var name: String ="";
  def getName(): String ={
    name
  }
  def setName(name: String):Unit = {
    this.name = name
  }
}
object SanMaoOps{
  def main(args: Array[String]): Unit ={
    var sm = new SanMaoScala;
    sm.setName("sanmao")
    println(sm.getName())
  }

}

在Scala中,getter和setter分别叫做age和age_=例如:

val personA = new PersonA
println("startAge=> " + personA.age)
personA.age = 21
println("endAge=> " + personA.age)
Scalac PersonOps.scala
\src\main\scala\tw\tw>javap -p PersonA.class
Compiled from "PersonOps.scala"
public class tw.PersonA {
  private int age;
  public int age();
  public void age_$eq(int);
  public tw.PersonA();
}

正如你看到的那样,编译器创建了age和age_ eq= eq,是因为JVM不允许在方法名中出现=
说明:在Scala中,getter和setter方法并非被命名为getXxx和setXxx,不过它们的用意是相同的。后面会介绍如何生成Java风格的getXxx和setXxx方法,以使得你的Scala类可以与Java工具实现互操作

3、如果字段是val,则只有getter方法被生成

有时候你需要一个只读属性,有getter但没有setter。如果属性的值在对象构建完成后就不再改变,则可以使用val字段:
//定义val声明的变量
val totalCount = 0
调用:custPerson.totalCount = 11(会抛出错误)
结论:Scala会生成一个私有的final字段和一个getter方法,但没有setter。
2和3)的一个小总结:
总结一下,在实现属性时你有如下四个选择:
  ■ var totalCount : Scala自动合成一个getter和一个setter
  ■ val totalCount : Scala自动合成一个getter
  ■ 由你来定义totalCount和totalCount _=方法
  ■ 由你来定义totalCount方法
但在Scala中,你不能实现只写属性,即带有setter但不带getter的属性。当你在Scala类中看到字段的时候,记住它和Java或c++中的字段不同。它是一个私有字段,加上getter方法(对val字段而言)或者getter和setter了法(对var字段而言)

4、私有字段:分为两种,1个是类私有字段,另外一个是对象私有字段

1)在Scala中Java和C++也一样,方法可以访问该类的所有对象的私有字段。例如:
/**
  * 类私有字段和对象私有字段的实现
  */
class PersonCounter{
  private var value = 0
  def increment () {value+=1 }
  // 可以访问另一个对象的私有字段
  def isLess (other: PersonCounter) = value < other.value 
}
2)对象私有字段

如果你不需要任何getter或setter,可以将字段声明为private[this]
private[this] val totalCount = 0

//这是不允许的方式(因为跨实例访问)
  def compareAgeBetweenDiffPersons(personC : PersonCounter): Unit ={
    if(totalAge > personC.totalAge){**//不能应用在别的实例上面,只能应用在此单一的实例上面**
      println("")
    }
  }

三、用@BeanProperty注解

1、Scala对于你定义的字段提供了getter和setter方法。不过,这些方法的名称并不是Java工具所预期的。

把Java属性定义为一对getName/setName方法或者对于只读属性而言单个getName方法。许多Java工具都依赖这样的命名习惯。当你将Scala字段标注为@BeanProperty时,这样的方法会自动生成。例如:

/**
  * 用BeanProperty实现的getter和setter方法
  */
import scala.reflect.BeanProperty
class BeanPropertyPerson{
  @BeanProperty var  name:String = _
}

调用:

val beanPropertyPerson = new BeanPropertyPerson()
beanPropertyPerson.setName("jack")
println("beanPropertyPerson.name=> " + beanPropertyPerson.getName)

beanPropertyPerson.name = "Garry"
println("scala.beanProperty.person.name=> " + beanPropertyPerson.name)

将会生成四个方法:
  ■ name:String
  ■ name_=(newValue: Strmg):Unit
  ■ getName():String
  ■ setName(newValue: String): Unit

一、Scala单例对象

1、Scala没有静态方法或静态字段,你可以用object这个语法结构来达到同样目的。对象定义了某个类的单个实例,包含了你想要的特性。例如

object PersonSingle{
  private var personNumber = 0
  def newUniquePersonNumber:Int = {
    personNumber+=1
    personNumber
  }
}
object ObjectOps {
  def main(args: Array[String]) {
    println(PersonSingle.newUniquePersonNumber)
    println(PersonSingle.newUniquePersonNumber)
  }
}

二、Scala伴生对象

在Java或C++中,你通常会用到既有实例方法又有静态方法的类。在Scala中,你可以通过类和与类同名的”伴生”对象来达到同样的目的。例如:

class PersonSingle{
  val personId = PersonSingle.newUniquePersonNumber
  private var personBalance:Double =0
  def personDeposit (amount: Double) { personBalance += amount }
}

类和它的伴生对象可以相互访问私有特性。它们必须存在于同一个源文件中。这说明了类的伴生对象可以被访问,但并不在作用域当中。举例来说, PersonSingle类必须通过

PersonSingle.newUniquePersonNumber
而不是直接用newUniqueNumber来调用伴生对象的方法。

三、对象可以扩展类或特质

1、一个object可以扩展类以及一个或多个特质,其结果是一个扩展了指定类以及特质的类的对象,同时拥有在对象定义中给出的所有特性。

abstract class PersonAccount (val accountName: String) {
  def unRegisterPersonAccount() : Unit
  def reRegisterPersonAccount() : Unit
}
object PersonAccountImpl extends PersonAccount("AccountName") {
  override def unRegisterPersonAccount (): Unit = {
    println("===start to logger======")
  }
  override def reRegisterPersonAccount (): Unit = {
    println("===end to logger=========")
  }
}
调用:PersonAccountImpl.reRegisterPersonAccount()

四、Apply方法(object和class)

1、我们通常会定义和使用对象的apply方法。当遇到如下形式的表达式时,apply方法就会被调用:

object(参数1,…,参数N)
通常,这样—个apply方法返回的是伴生类的对象

class PersonApplyTest{
  private var balance = 0
  def apply() = println("your person apply class test")

  def initPersonNumber:Int = {
    balance += 1
    balance
  }
}
object PersonApplyTest{
  def apply () = {
    println("Please init your person information")
    new PersonApplyTest
  }
}
调用:
1val personApplyTest =  PersonApplyTest()
    println(personApplyTest.initPersonNumber)

2val personApplyTest = new PersonApplyTest
    println(personApplyTest.initPersonNumber)
    personApplyTest()

五、扩展App类的特征

1、除了每次都提供自己的main方法外,你也可以扩展App特质,然后将程序代码放人构造器方法体内:

object ObjectOps extends  App{
  if (args.length > 0)
    println("Hello, "+args (0))
  else
    println("Hello, World! ")
}

六、枚举Enumeration

1、Scala并没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举。定义一个扩展Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值。

2、枚举定义的初始化

object PersonClothesColor extends Enumeration{
val Red,Yellow,Green = Value
}

3、枚举引用

1)定义完成后,你就可以用PersonClothesColor.Red、PersonClothesColor.Yellow等来引用枚举值了。
2)需要注意的是:枚举的类型是PersonClothesColor.Value而不是PersonClothesColor,后者是握有这些值的对象。有人推荐增加一个类型别名:

4、访问枚举

for(c <- PersonClothesColor.values){
println(c.id +”:” + c)
}

println(PersonClothesColor(0))
println(PersonClothesColor.withName(“Yellow”))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值