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

in cn •  7 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


知识星球交流群

知识星球读者交流群

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Hi! I am a robot. I just upvoted you! I found similar content that readers might be interested in:
http://www.cnblogs.com/HeQiangJava/p/6711508.html