定义一个简单的类
1// 定义类,包含field及方法
2scala> :paste
3// Entering paste mode (ctrl-D to finish)
4class HelloWorld {
5 private var name = "leo"
6 def sayHello() {print("Hello, " + name)}
7 def getName = name
8}
9// Exiting paste mode, now interpreting.
10defined class HelloWorld
11// 创建类的对象,并调用其方法
12scala> val helloWorld = new HelloWorld
13helloWorld: HelloWorld = HelloWorld@380e4452
14// 如果方法无参,可以不加括号,如果定义方法时不带括号,则调用方法时也不能带括号
15scala> helloWorld.sayHello()
16Hello, leo
17scala> helloWorld.sayHello
18Hello, leo
19scala> helloWorld.getName
20res9: String = leo
21scala> helloWorld.getName()
22<console>:14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
23Unspecified value parameter index.
24 helloWorld.getName()
25 ^
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]。
1scala> class Student {
2 | var name = "leo"
3 | }
4defined class Student
5scala> val s = new Student
6s: Student = Student@2a88981e
7// 调用getter和setter方法,分别叫做name和name_=
8scala> s.name
9res0: String = leo
10scala> s.name()
11<console>:14: error: not enough arguments for method apply: (index: Int)Char in class StringOps.
12Unspecified value parameter index.
13 s.name()
14 ^
15scala> s.name = "jack"
16s.name: String = jack
17scala> s.name
18res2: String = jack
19scala> s.name_ = "jen"
20<console>:15: error: value name_ is not a member of Student
21val $ires1 = s.name_
22 ^
23<console>:13: error: value name_ is not a member of Student
24 s.name_ = "jen"
25 ^
自定义getter与setter
如果只是希望拥有简单的getter和setter方法,那么就按照Scala提供的语法规则,根据需求为field选择合适的修饰符就好:var、val、private、private[this]。
如果希望能够自己对getter与setter进行控制,则可以自定义getter与setter方法。
自定义setter方法的时候一定要注意Scala的语法限制,签名、=、参数间不能有空格。
1scala> :paste
2// Entering paste mode (ctrl-D to finish)
3class Student {
4 private var myName = "leo"
5 def name = "your name is " + myName
6 def name_ = (newName:String) {
7 print("you cannot edit your name!")
8 }
9}
10// Exiting paste mode, now interpreting.
11<console>:14: error: not found: value newName
12 def name_ = (newName:String) {
13 ^
14scala> :paste
15// Entering paste mode (ctrl-D to finish)
16class Student {
17 private var myName = "leo"
18 def name = "your name is " + myName
19 def name_=(newName:String) {
20 print("you cannot edit your name!")
21 }
22}
23// Exiting paste mode, now interpreting.
24defined class Student
25scala> val s = new Student
26s: Student = Student@4fc8cd5c
27scala> val s = new Student()
28s: Student = Student@1dafc862
29scala> s.name
30res3: String = your name is leo
31scala> s.name = "leo1"
32you 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方法。
1scala> :paste
2// Entering paste mode (ctrl-D to finish)
3class Student {
4 private var myName = "leo"
5 def updateName(newName:String) {
6 if(newName=="leo1") myName = newName
7 else println("not accept this new name, " + newName)
8 }
9 def name = "your name is " + myName
10}
11// Exiting paste mode, now interpreting.
12defined class Student
13scala> val s = new Student
14s: Student = Student@3186acfb
15scala> s.name
16res4: String = your name is leo
17scala> s.updateName("leo2")
18not accept this new name, leo2
19scala> s.updateName("leo1")
20scala> s.name
21res7: String = your name is leo1
private[this]的使用
如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以访问类的其他对象的private field。这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对象私有的field,只有本对象内可以访问到。
1scala> :paste
2// Entering paste mode (ctrl-D to finish)
3class Student {
4 private var myAge = 0
5 def age_=(newAge:Int) {
6 if(newAge>0) myAge = newAge
7 else println("illegal age!")
8 }
9 def age = myAge
10 def older(s:Student) = {
11 myAge > s.myAge
12 }
13}
14// Exiting paste mode, now interpreting.
15defined class Student
16scala> val s1 = new Student
17s1: Student = Student@7ba9d3ec
18scala> s1.age = 20
19s1.age: Int = 20
20scala> val s2 = new Student
21s2: Student = Student@2dd6713
22scala> s2.age = 25
23s2.age: Int = 25
24scala> s1.older(s2)
25res8: Boolean = false
private[this]的使用,只有本对象内可以访问到。
1scala> :paste
2// Entering paste mode (ctrl-D to finish)
3class Student {
4 private[this] var myAge = 0
5 def age_=(newAge:Int) {
6 if(newAge>0) myAge = newAge
7 else println("illegal age!")
8 }
9 def age = myAge
10 def older(s:Student) = {
11 myAge > s.myAge
12 }
13}
14// Exiting paste mode, now interpreting.
15<console>:23: error: value myAge is not a member of Student
16 myAge > s.myAge
17 ^
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
。
1scala> import scala.reflect.BeanProperty
2<console>:13: error: object BeanProperty is not a member of package reflect
3 import scala.reflect.BeanProperty
4 ^
5scala> import scala.beans.BeanProperty
6import scala.beans.BeanProperty
7scala> :paste
8// Entering paste mode (ctrl-D to finish)
9class Student {
10 @BeanProperty var name:String = _
11}
12// Exiting paste mode, now interpreting.
13defined class Student
14scala> val s = new Student
15s: Student = Student@1828b826
16scala> s.setName("leo")
17scala> s.getName()
18res10: String = leo
19scala> s.name = "jack"
20s.name: String = jack
21scala> s.name
22res12: String = jack
在主构造函数方式加注解,
1scala> class Student(@BeanProperty var name:String)
2defined class Student
3scala> val s = new Student("leo")
4s: Student = Student@148bf213
5scala> s.getName()
6res17: String = leo
7scala> s.setName("jack")
8scala> s.getName
9res19: String = jack
10scala> s.name
11res20: String = jack
辅助constructor
Scala中,可以给类定义多个辅助constructor,类似于Java中的构造函数重载,辅助constructor之间可以互相调用,而且必须第一行调用主constructor。
1scala> :paste
2// Entering paste mode (ctrl-D to finish)
3class Student {
4 private var name = ""
5 private var age = 0
6 def this(name:String) {
7 this()
8 this.name = name
9 }
10 def this(name:String, age:Int) {
11 this(name)
12 this.age = age
13 }
14}
15// Exiting paste mode, now interpreting.
16defined class Student
17scala> val s1 = new Student
18s1: Student = Student@5eb6d96d
19scala> val s2 = new Student("leo")
20s2: Student = Student@b34c972
21scala> val s3 = new Student("leo", 30)
22s3: Student = Student@1182aa34
主constructor
Scala中,主constructor是与类名放在一起的,与Java不同,而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有Java那么清晰。
1scala> class Student(val name:String, val age:Int) {
2 | println("your name is " + name + ", your age is " + age)
3 | }
4defined class Student
5scala> val s = new Student
6<console>:14: error: not enough arguments for constructor Student: (name: String, age: Int)Student.
7Unspecified value parameters name, age.
8 val s = new Student
9 ^
10scala> val s = new Student("leo", 30)
11your name is leo, your age is 30
12s: Student = Student@60cdee08
主construntor中还可以通过使用默认参数,来给参数默认的值。
1scala> class Student(val name:String="leo", val age:Int=30) {
2 | println("your name is " + name + ", your age is " + age)
3 | }
4defined class Student
5scala> val s = new Student
6your name is leo, your age is 30
7s: Student = Student@79eef059
如果主constructor传入的参数什么修饰都没有,比如name:String,那么如果类内部的方法使用到了,则会声明为private[this] name,否则没有该field,就只能被constructor代码使用而已。
1scala> class Student(name:String="leo", age:Int=30) {
2 | println("your name is " + name + ", your age is " + age)
3 | }
4defined class Student
5scala> val s = new Student("leo", 30)
6your name is leo, your age is 30
7s: Student = Student@115989a9
8scala> s.name
9<console>:14: error: value name is not a member of Student
10 s.name
11 ^
内部类
Scala中,同样可以在类中定义内部类,但是与Java不同的是,每个外部类的对象的内部类,都是不同的类。
c2.Student类,c1.Student类,是不同的外部类的实例的不同的类。
1scala> :paste
2// Entering paste mode (ctrl-D to finish)
3import scala.collection.mutable.ArrayBuffer
4class Class {
5 class Student(val name:String)
6 val students = new ArrayBuffer[Student]()
7 def getStudent(name:String) = {
8 new Student(name)
9 }
10}
11// Exiting paste mode, now interpreting.
12import scala.collection.mutable.ArrayBuffer
13defined class Class
14scala> val c1 = new Class
15c1: Class = Class@7d6340d6
16scala> val s1
17 | = c1.getStudent("leo")
18s1: c1.Student = Class$Student@2662e5cf
19scala> c1.students += s1
20res1: c1.students.type = ArrayBuffer(Class$Student@2662e5cf)
21scala> val c2 = new Class
22c2: Class = Class@d207f78
23scala> val s2 = c2.getStudent("leo")
24s2: c2.Student = Class$Student@56de11b8
25scala> c1.students += s2
26<console>:19: error: type mismatch;
27 found : c2.Student
28 required: c1.Student
29 c1.students += s2
30 ^
object
object,相当于class的单个实例,通常在里面放一些静态的field或者method,第一次调用object的方法时,就会执行object的constructor,也就是object内部不在method中的代码,但是object不能定义接受参数的constructor。
object的constructor只会在其第一次被调用时执行一次,以后再次调用就不会再次执行constructor了。
object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法。
1object Person {
2 private var eyeNum = 2
3 println("this is Person object constructor is execting")
4 def getEyeNum = eyeNum
5}
6scala> Person.getEyeNum
7this is Person object constructor is execting
8res4: Int = 2
9scala> Person.getEyeNum
10res5: Int = 2
伴生对象
如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类。伴生类和伴生对象必须存放在一个.scala文件之中,伴生类和伴生对象,最大的特点在于,互相可以访问private field。
1class Person(val name:String, val age:Int) {
2 def sayHello = println("Hi, " + name + "is" + age + "years old" + ", and you have " + Person.eyeNum + " eyes.")
3}
4object Person {
5 private val eyeNum = 2
6 def getEyeNum = eyeNum
7}
8scala> val p = new Person("leo", 30)
9p: Person = Person@6b1e9a68
10scala> p.sayHello
11Hi, leois30years old, and you have 2 eyes.
12# 伴生类里面可以访问伴生对象的private field,在外面不行。
13scala> Person.eyeNum
14<console>:15: error: value eyeNum is not a member of object Person
15 Person.eyeNum
16 ^
object继承抽象类
object的功能和class类似,除了不能定义接收参数的constructor之外,object也可以继承抽象类,并覆盖抽象类中的方法。
1abstract class Hello(var message:String) {
2 def sayHello(name:String): Unit
3}
4object HelloImpl extends Hello("hello") {
5 override def sayHello(name:String) = {
6 println(message + "," + name)
7 }
8}
9scala> HelloImpl.sayHello("leo")
10hello,leo
apply方法
object中重要的一个特殊方法,apply方法,通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能。而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式调用伴生对象的apply方法,让对象创建更简洁。如Array类的伴生对象的apply方法就实现了接收可变数量的参数,并创建一个Array对象的功能。
1scala> val a = Array(1,2,3)
2a: Array[Int] = Array(1, 2, 3)
3//
4class Person(val name:String)
5object Person {
6 def apply(name:String) = new Person(name)
7}
8scala> val p1 = new Person("leo")
9p1: Person = Person@78f71925
10scala> val p2 = Person("leo")
11p2: Person = Person@d5b8b3f
12scala> p2.name
13res10: String = leo
main方法
在Scala中,main方法作为应用程序的入口,Scala中的main方法定义为def main(args:Array[String])
,而且必须定义在object中。
1object HelloWorld {
2 def main(args:Array[String]) {
3 println("Hello World!")
4 }
5}
6scala> HelloWorld.main(_)
7res15: Array[String] => Unit = <function1>
8
除了自己实现main方法,还可以继承App Train,然后将需要在main方法中运行的代码,直接作为object的constructor代码,而且用args可以接受传入的参数。
1object HelloWorld extends App {
2 if (args.length > 0) println("hello, " + args(0))
3 else println("Hello, World!")
4}
AppTrait继承自DelayedInit Trait,scalac命令进行编译时,会把继承App Trait的object的consturctor代码都放到DelayedInit Trait的delayedInit方法中执行。
用object实现枚举功能
Scala没有直接提供类似于Java的Enum枚举特性,如果要实现枚举,则需要用object继承Enumeration,并且调用Value方法来初始化枚举值。
1object Season extends Enumeration {
2 val SPRING, SUMMER, AUTUMN, WINTER = Value
3}
4scala> Season.SPRING
5res1: Season.Value = SPRING
还可以通过Value传入枚举值的id和name,通过id和toString可以获取,还可以通过id和name来查找枚举值。
1object Season extends Enumeration {
2 val SPRING = Value(0, "spring")
3 val SUMMER = Value(1, "summer")
4 val AUTUMN = Value(2, "autumn")
5 val WINTER = Value(3, "winter")
6}
7scala> Season.SPRING.id
8res2: Int = 0
9scala> Season.SPRING.toString
10res3: String = spring
11scala> Season(0)
12res4: Season.Value = spring
13scala> Season.withName("winter")
14res5: Season.Value = winter
15scala> for(ele <- Season.values) println(ele)
16spring
17summer
18autumn
19winter
本文首发于steem,感谢阅读,转载请注明。
微信公众号「padluo」,分享数据科学家的自我修养,既然遇见,不如一起成长。
读者交流电报群
知识星球交流群