Scala学习笔记05_面向对象编程之类和对象
定义一个简单的类
// 定义类,包含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是不同的,是field
和field_=
的方式。如果要让Scala自动生成Java风格的getter和setter方法,只要给field添加@BeanProperty
注解即可。此时会生成4个方法,name:String
、name_=(newValue:String):Unit
、getName():String
,setName(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,感谢阅读,转载请注明。
微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。
读者交流电报群
知识星球交流群