Scala学习笔记05_面向对象编程之类和对象

in #cn7 years ago (edited)

定义一个简单的类

// 定义类,包含field及方法
scala> :paste
// Entering paste mode (ctrl-D to finish)

class HelloWorld {
  private var name = "leo"
  def sayHello() {print("Hello, " + name)}
  def getName = name
}

// Exiting paste mode, now interpreting.

defined class HelloWorld
// 创建类的对象,并调用其方法
scala> val helloWorld = new HelloWorld
helloWorld: HelloWorld = HelloWorld@380e4452
// 如果方法无参,可以不加括号,如果定义方法时不带括号,则调用方法时也不能带括号
scala> helloWorld.sayHello()
Hello, leo
scala> helloWorld.sayHello
Hello, leo
scala> helloWorld.getName
res9: String = leo

scala> helloWorld.getName()
<console>:14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
Unspecified value parameter index.
       helloWorld.getName()
                         ^

getter与setter

定义不带private的var field,此时scala生成的面向JVM的类时,会定义为private的name字段,并提供public的getter和setter方法。

而如果使用private修饰field,则生成的getter和setter也是private的。

如果定义val field,则只会生成getter方法。

如果不希望生成setter和getter方法,则将field声明为private[this]。

scala> class Student {
     | var name = "leo"
     | }
defined class Student

scala> val s = new Student
s: Student = Student@2a88981e
// 调用getter和setter方法,分别叫做name和name_=
scala> s.name
res0: String = leo

scala> s.name()
<console>:14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
Unspecified value parameter index.
       s.name()
             ^

scala> s.name = "jack"
s.name: String = jack

scala> s.name
res2: String = jack

scala> s.name_ = "jen"
<console>:15: error: value name_ is not a member of Student
val $ires1 = s.name_
               ^
<console>:13: error: value name_ is not a member of Student
       s.name_ = "jen"
         ^

自定义getter与setter

如果只是希望拥有简单的getter和setter方法,那么就按照Scala提供的语法规则,根据需求为field选择合适的修饰符就好:var、val、private、private[this]。

如果希望能够自己对getter与setter进行控制,则可以自定义getter与setter方法。

自定义setter方法的时候一定要注意Scala的语法限制,签名、=、参数间不能有空格。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student {
  private var myName = "leo"
  def name = "your name is " + myName
  def name_ = (newName:String) {
    print("you cannot edit your name!")
  }
}

// Exiting paste mode, now interpreting.

<console>:14: error: not found: value newName
         def name_ = (newName:String) {
                      ^

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student {
  private var myName = "leo"
  def name = "your name is " + myName
  def name_=(newName:String) {
    print("you cannot edit your name!")
  }
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s = new Student
s: Student = Student@4fc8cd5c

scala> val s = new Student()
s: Student = Student@1dafc862

scala> s.name
res3: String = your name is leo

scala> s.name = "leo1"
you cannot edit your name!s.name: String = your name is leo

仅暴露field的getter方法

如果不希望field有setter方法,则可以定义为val,但是此时就再也不能更改field的值了。

如果希望能够仅仅暴露出一个getter方法,并且还能通过某些方法更改field的值,那么需要综合使用private以及自定义getter方法。此时,由于field是private的,所以setter和getter都是private,对外界没有暴露,自己可以实现修改field值的方法,自己可以覆盖getter方法。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student {
  private var myName = "leo"

  def updateName(newName:String) {
    if(newName=="leo1") myName = newName
    else println("not accept this new name, " + newName)
  }

  def name = "your name is " + myName
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s = new Student
s: Student = Student@3186acfb

scala> s.name
res4: String = your name is leo

scala> s.updateName("leo2")
not accept this new name, leo2

scala> s.updateName("leo1")

scala> s.name
res7: String = your name is leo1

private[this]的使用

如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以访问类的其他对象的private field。这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对象私有的field,只有本对象内可以访问到。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student {
  private var myAge = 0
  def age_=(newAge:Int) {
    if(newAge>0) myAge = newAge
    else println("illegal age!")
  }
  def age = myAge
  def older(s:Student) = {
    myAge > s.myAge
  }
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s1 = new Student
s1: Student = Student@7ba9d3ec

scala> s1.age = 20
s1.age: Int = 20

scala> val s2 = new Student
s2: Student = Student@2dd6713

scala> s2.age = 25
s2.age: Int = 25

scala> s1.older(s2)
res8: Boolean = false

private[this]的使用,只有本对象内可以访问到。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student {
  private[this] var myAge = 0
  def age_=(newAge:Int) {
    if(newAge>0) myAge = newAge
    else println("illegal age!")
  }
  def age = myAge
  def older(s:Student) = {
    myAge > s.myAge
  }
}

// Exiting paste mode, now interpreting.

<console>:23: error: value myAge is not a member of Student
           myAge > s.myAge
                     ^

Java风格的getter和setter方法

Scala的getter和setter方法的命名与Java是不同的,是fieldfield_=的方式。如果要让Scala自动生成Java风格的getter和setter方法,只要给field添加@BeanProperty注解即可。此时会生成4个方法,name:Stringname_=(newValue:String):UnitgetName():StringsetName(newValue:String):Unit

scala> import scala.reflect.BeanProperty
<console>:13: error: object BeanProperty is not a member of package reflect
       import scala.reflect.BeanProperty
              ^

scala> import scala.beans.BeanProperty
import scala.beans.BeanProperty

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student {
  @BeanProperty var name:String = _
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s = new Student
s: Student = Student@1828b826

scala> s.setName("leo")

scala> s.getName()
res10: String = leo

scala> s.name = "jack"
s.name: String = jack

scala> s.name
res12: String = jack

在主构造函数方式加注解,

scala> class Student(@BeanProperty var name:String)
defined class Student

scala> val s = new Student("leo")
s: Student = Student@148bf213

scala> s.getName()
res17: String = leo

scala> s.setName("jack")

scala> s.getName
res19: String = jack

scala> s.name
res20: String = jack

辅助constructor

Scala中,可以给类定义多个辅助constructor,类似于Java中的构造函数重载,辅助constructor之间可以互相调用,而且必须第一行调用主constructor。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Student {
  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
  }
}

// Exiting paste mode, now interpreting.

defined class Student

scala> val s1 = new Student
s1: Student = Student@5eb6d96d

scala> val s2 = new Student("leo")
s2: Student = Student@b34c972

scala> val s3 = new Student("leo", 30)
s3: Student = Student@1182aa34

主constructor

Scala中,主constructor是与类名放在一起的,与Java不同,而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有Java那么清晰。

scala> class Student(val name:String, val age:Int) {
     |   println("your name is " + name + ", your age is " + age)
     | }
defined class Student

scala> val s = new Student
<console>:14: error: not enough arguments for constructor Student: (name: String, age: Int)Student.
Unspecified value parameters name, age.
       val s = new Student
               ^

scala> val s = new Student("leo", 30)
your name is leo, your age is 30
s: Student = Student@60cdee08

主construntor中还可以通过使用默认参数,来给参数默认的值。

scala> class Student(val name:String="leo", val age:Int=30) {
     |   println("your name is " + name + ", your age is " + age)
     | }
defined class Student

scala> val s = new Student
your name is leo, your age is 30
s: Student = Student@79eef059

如果主constructor传入的参数什么修饰都没有,比如name:String,那么如果类内部的方法使用到了,则会声明为private[this] name,否则没有该field,就只能被constructor代码使用而已。

scala> class Student(name:String="leo", age:Int=30) {
     |   println("your name is " + name + ", your age is " + age)
     | }
defined class Student

scala> val s = new Student("leo", 30)
your name is leo, your age is 30
s: Student = Student@115989a9

scala> s.name
<console>:14: error: value name is not a member of Student
       s.name
         ^

内部类

Scala中,同样可以在类中定义内部类,但是与Java不同的是,每个外部类的对象的内部类,都是不同的类。

c2.Student类,c1.Student类,是不同的外部类的实例的不同的类。

scala> :paste
// Entering paste mode (ctrl-D to finish)

import scala.collection.mutable.ArrayBuffer

class Class {
  class Student(val name:String)
  val students = new ArrayBuffer[Student]()
  def getStudent(name:String) = {
    new Student(name)
  }
}

// Exiting paste mode, now interpreting.

import scala.collection.mutable.ArrayBuffer
defined class Class

scala> val c1 = new Class
c1: Class = Class@7d6340d6

scala> val s1
     |  = c1.getStudent("leo")
s1: c1.Student = Class$Student@2662e5cf

scala> c1.students += s1
res1: c1.students.type = ArrayBuffer(Class$Student@2662e5cf)

scala> val c2 = new Class
c2: Class = Class@d207f78

scala> val s2 = c2.getStudent("leo")
s2: c2.Student = Class$Student@56de11b8

scala> c1.students += s2
<console>:19: error: type mismatch;
 found   : c2.Student
 required: c1.Student
       c1.students += s2
                      ^

object

object,相当于class的单个实例,通常在里面放一些静态的field或者method,第一次调用object的方法时,就会执行object的constructor,也就是object内部不在method中的代码,但是object不能定义接受参数的constructor。

object的constructor只会在其第一次被调用时执行一次,以后再次调用就不会再次执行constructor了。

object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法。

object Person {
  private var eyeNum = 2
  println("this is Person object constructor is execting")
  def getEyeNum = eyeNum
}

scala> Person.getEyeNum
this is Person object constructor is execting
res4: Int = 2

scala> Person.getEyeNum
res5: Int = 2

伴生对象

如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类。伴生类和伴生对象必须存放在一个.scala文件之中,伴生类和伴生对象,最大的特点在于,互相可以访问private field。

class Person(val name:String, val age:Int) {
  def sayHello = println("Hi, " + name + "is" + age + "years old" + ", and you have " + Person.eyeNum + " eyes.")
}

object Person {
  private val eyeNum = 2
  def getEyeNum = eyeNum
}

scala> val p = new Person("leo", 30)
p: Person = Person@6b1e9a68

scala> p.sayHello
Hi, leois30years old, and you have 2 eyes.
# 伴生类里面可以访问伴生对象的private field,在外面不行。
scala> Person.eyeNum
<console>:15: error: value eyeNum is not a member of object Person
       Person.eyeNum
              ^

object继承抽象类

object的功能和class类似,除了不能定义接收参数的constructor之外,object也可以继承抽象类,并覆盖抽象类中的方法。

abstract class Hello(var message:String) {
  def sayHello(name:String): Unit
}
object HelloImpl extends Hello("hello") {
  override def sayHello(name:String) = {
    println(message + "," + name)
  }
}

scala> HelloImpl.sayHello("leo")
hello,leo

apply方法

object中重要的一个特殊方法,apply方法,通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能。而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式调用伴生对象的apply方法,让对象创建更简洁。如Array类的伴生对象的apply方法就实现了接收可变数量的参数,并创建一个Array对象的功能。

scala> val a = Array(1,2,3)
a: Array[Int] = Array(1, 2, 3)

// 
class Person(val name:String)
object Person {
  def apply(name:String) = new Person(name)
}

scala> val p1 = new Person("leo")
p1: Person = Person@78f71925

scala> val p2 = Person("leo")
p2: Person = Person@d5b8b3f

scala> p2.name
res10: String = leo

main方法

在Scala中,main方法作为应用程序的入口,Scala中的main方法定义为def main(args:Array[String]),而且必须定义在object中。

object HelloWorld {
  def main(args:Array[String]) {
    println("Hello World!")
  }
}

scala> HelloWorld.main(_)
res15: Array[String] => Unit = <function1>

除了自己实现main方法,还可以继承App Train,然后将需要在main方法中运行的代码,直接作为object的constructor代码,而且用args可以接受传入的参数。

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

AppTrait继承自DelayedInit Trait,scalac命令进行编译时,会把继承App Trait的object的consturctor代码都放到DelayedInit Trait的delayedInit方法中执行。

用object实现枚举功能

Scala没有直接提供类似于Java的Enum枚举特性,如果要实现枚举,则需要用object继承Enumeration,并且调用Value方法来初始化枚举值。

object Season extends Enumeration {
  val SPRING, SUMMER, AUTUMN, WINTER = Value
}

scala> Season.SPRING
res1: Season.Value = SPRING

还可以通过Value传入枚举值的id和name,通过id和toString可以获取,还可以通过id和name来查找枚举值。

object Season extends Enumeration {
  val SPRING = Value(0, "spring")
  val SUMMER = Value(1, "summer")
  val AUTUMN = Value(2, "autumn")
  val WINTER = Value(3, "winter")
}

scala> Season.SPRING.id
res2: Int = 0

scala> Season.SPRING.toString
res3: String = spring

scala> Season(0)
res4: Season.Value = spring

scala> Season.withName("winter")
res5: Season.Value = winter

scala> for(ele <- Season.values) println(ele)
spring
summer
autumn
winter

本文首发于steem,感谢阅读,转载请注明。

https://steemit.com/@padluo


微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。

数据分析


读者交流电报群

https://t.me/sspadluo


知识星球交流群

知识星球读者交流群

Coin Marketplace

STEEM 0.20
TRX 0.25
JST 0.038
BTC 96601.54
ETH 3445.82
USDT 1.00
SBD 3.09