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

    xiaoxiao2021-09-10  102

    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”))

    转载请注明原文地址: https://ju.6miu.com/read-677529.html

    最新回复(0)