定義一個(gè)簡單的類
// 定義類,包含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
// 創(chuàng)建類的對(duì)象蚯窥,并調(diào)用其方法
scala> val helloWorld = new HelloWorld
helloWorld: HelloWorld = HelloWorld@380e4452
// 如果方法無參葵姥,可以不加括號(hào)颊咬,如果定義方法時(shí)不帶括號(hào),則調(diào)用方法時(shí)也不能帶括號(hào)
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麸澜,此時(shí)scala生成的面向JVM的類時(shí)熟史,會(huì)定義為private的name字段碘菜,并提供public的getter和setter方法。
而如果使用private修飾field计雌,則生成的getter和setter也是private的。
如果定義val field眷细,則只會(huì)生成getter方法。
如果不希望生成setter和getter方法校读,則將field聲明為private[this]歉秫。
scala> class Student {
| var name = "leo"
| }
defined class Student
scala> val s = new Student
s: Student = Student@2a88981e
// 調(diào)用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提供的語法規(guī)則褒链,根據(jù)需求為field選擇合適的修飾符就好:var甫匹、val哀墓、private、private[this]吠各。
如果希望能夠自己對(duì)getter與setter進(jìn)行控制,則可以自定義getter與setter方法纵散。
自定義setter方法的時(shí)候一定要注意Scala的語法限制伍掀,簽名蜜笤、=把兔、參數(shù)間不能有空格县好。
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善绎,但是此時(shí)就再也不能更改field的值了禀酱。
如果希望能夠僅僅暴露出一個(gè)getter方法剂跟,并且還能通過某些方法更改field的值曹洽,那么需要綜合使用private以及自定義getter方法。此時(shí)税产,由于field是private的辟拷,所以setter和getter都是private衫冻,對(duì)外界沒有暴露隅俘,自己可以實(shí)現(xiàn)修改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來修飾董朝,那么代表這個(gè)field是類私有的祟绊,在類的方法中遥赚,可以訪問類的其他對(duì)象的private field孕惜。這種情況下,如果不希望field被其他對(duì)象訪問到,那么可以使用private[this]丰榴,意味著對(duì)象私有的field,只有本對(duì)象內(nèi)可以訪問到戈二。
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]的使用仆邓,只有本對(duì)象內(nèi)可以訪問到搞疗。
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風(fēng)格的getter和setter方法
Scala的getter和setter方法的命名與Java是不同的扳埂,是field
和field_=
的方式柜思。如果要讓Scala自動(dòng)生成Java風(fēng)格的getter和setter方法,只要給field添加@BeanProperty
注解即可。此時(shí)會(huì)生成4個(gè)方法抛姑,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
在主構(gòu)造函數(shù)方式加注解,
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中,可以給類定義多個(gè)輔助constructor,類似于Java中的構(gòu)造函數(shù)重載,輔助constructor之間可以互相調(diào)用裹虫,而且必須第一行調(diào)用主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的代碼,這點(diǎn)感覺沒有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中還可以通過使用默認(rèn)參數(shù)雌澄,來給參數(shù)默認(rèn)的值。
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傳入的參數(shù)什么修飾都沒有旗唁,比如name:String参袱,那么如果類內(nèi)部的方法使用到了,則會(huì)聲明為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
^
內(nèi)部類
Scala中邓梅,同樣可以在類中定義內(nèi)部類殿遂,但是與Java不同的是,每個(gè)外部類的對(duì)象的內(nèi)部類,都是不同的類邑飒。
c2.Student類风科,c1.Student類,是不同的外部類的實(shí)例的不同的類顶瞳。
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,相當(dāng)于class的單個(gè)實(shí)例备蚓,通常在里面放一些靜態(tài)的field或者method扎即,第一次調(diào)用object的方法時(shí)谚鄙,就會(huì)執(zhí)行object的constructor闷营,也就是object內(nèi)部不在method中的代碼傻盟,但是object不能定義接受參數(shù)的constructor规哲。
object的constructor只會(huì)在其第一次被調(diào)用時(shí)執(zhí)行一次唉锌,以后再次調(diào)用就不會(huì)再次執(zhí)行constructor了糊秆。
object通常用于作為單例模式的實(shí)現(xiàn),或者放class的靜態(tài)成員汞舱,比如工具方法。
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
伴生對(duì)象
如果有一個(gè)class,還有一個(gè)與class同名的object欢际,那么就稱這個(gè)object是class的伴生對(duì)象损趋,class是object的伴生類桐玻。伴生類和伴生對(duì)象必須存放在一個(gè).scala文件之中,伴生類和伴生對(duì)象褪子,最大的特點(diǎn)在于,互相可以訪問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.
# 伴生類里面可以訪問伴生對(duì)象的private field,在外面不行。
scala> Person.eyeNum
<console>:15: error: value eyeNum is not a member of object Person
Person.eyeNum
^
object繼承抽象類
object的功能和class類似,除了不能定義接收參數(shù)的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中重要的一個(gè)特殊方法,apply方法忙厌,通常在伴生對(duì)象中實(shí)現(xiàn)apply方法,并在其中實(shí)現(xiàn)構(gòu)造伴生類的對(duì)象的功能挟阻。而創(chuàng)建伴生類的對(duì)象時(shí),通常不會(huì)使用new Class的方式惯雳,而是使用Class()的方式,隱式調(diào)用伴生對(duì)象的apply方法挨决,讓對(duì)象創(chuàng)建更簡潔眼虱。如Array類的伴生對(duì)象的apply方法就實(shí)現(xiàn)了接收可變數(shù)量的參數(shù),并創(chuàng)建一個(gè)Array對(duì)象的功能庙洼。
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方法作為應(yīng)用程序的入口,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>
除了自己實(shí)現(xiàn)main方法围来,還可以繼承App Train醇滥,然后將需要在main方法中運(yùn)行的代碼购城,直接作為object的constructor代碼,而且用args可以接受傳入的參數(shù)薄坏。
object HelloWorld extends App {
if (args.length > 0) println("hello, " + args(0))
else println("Hello, World!")
}
AppTrait繼承自DelayedInit Trait矮瘟,scalac命令進(jìn)行編譯時(shí)写妥,會(huì)把繼承App Trait的object的consturctor代碼都放到DelayedInit Trait的delayedInit方法中執(zhí)行症脂。
用object實(shí)現(xiàn)枚舉功能
Scala沒有直接提供類似于Java的Enum枚舉特性,如果要實(shí)現(xiàn)枚舉,則需要用object繼承Enumeration歪沃,并且調(diào)用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
本文首發(fā)于steem魔策,感謝閱讀擂橘,轉(zhuǎn)載請(qǐng)注明灾馒。
微信公眾號(hào)「padluo」古涧,分享數(shù)據(jù)科學(xué)家的自我修養(yǎng),既然遇見昔字,不如一起成長啸罢。
讀者交流電報(bào)群
知識(shí)星球交流群