一、Scala中的类实战详解类跟Java一样,Scala也是用关键字class来定义类。示例如下:
scala> class HiScala{
| private var name = "Spark"
| def sayName(){println(name)}
| def getName = name
| }
defined class HiScala
以上代码,定义了一个名称为HiScala的类,默认情况下是public级别,所以public关键字可以不写。
ü 定义了一个属性name,为可变变量,访问级别为private,在类的外面不能被访问!
ü 定义一个函数:sayName
ü 定义一个函数:getName
在Scala中,变量与类中的方法是同等级的,可以直接相互赋值。
创建类实例
scala> val scal = new HiScala
scal: HiScala = HiScala@1c655221
//此时,scal就是HiScala类的一个实例。但是,在Scala中我们一边不会用new来创建类的实例,而是用apply工厂方法模式来创建。
//调用scal的sayName方法,用于打印出name成员的值。
scala> scal.sayName()
Spark
//由于sayName没有传参数,所以可以把括号去掉,这样更简洁。
scala> scal.sayName
Spark
//调用getName方法,访问name成员的值
scala> scal.getName
res2: String = Spark
//此时确实返回了name的值。
//该示例中name是私有的,所以不能直接访问scal实例的name,如下访问就出错了。
scala> scal.name
<console>:10: error: variable name inclass HiScala cannot be accessed in HiScala
scal.name
^
get与setScala的get和set跟Java的get和set有很大的差异。在Scala中,如果给变量前定义了private,那么Scala解释器会给这个变量自动生成private的get和set方 法;如果变量前没有定义了private,那么解释器会给这个变量自动生成public的get和set方法,这样就可以直接访问该类的实例对象的成员变量,实际访问的是它的set和get方法。
//我们重新改造上面的类,把属性name前面的private访问级别去掉。
scala> class HiScala{
| var name ="Spark" //去掉了private关键字
| defsayName(){println(name)}
| def getName = name
| }
defined class HiScala
scala> val scal = new HiScala
scal: HiScala = HiScala@1e6d1014
//访问name属性, 值为Spark
scala> scal.name
res4: String = Spark
//此时虽然是访问属性name,但其实不是直接访问var指定的变量name,而是Scala解释器自动给name生成public级别的get和set方法。
//修改name属性
scala> scal.name="Scala"
scal.name: String = Scala
//再次访问name属性, 值已经变为Scala
scala> scal.name
res5: String = Scala
自定义的get和setscala> class Person {
| private var myName ="Flink"
| def name = this.myName //自定义get方法
| def name_=(newName : String){ //自定义set方法,注意下划线和等号之间没有空格!
| myName = newName
| println("Hi " + myName)
| }
| }
defined class Person
// luck为Person 类的实例
scala> val luck = new Person
luck: Person = Person@6a5fc7f7
//通过自定义的get方法,访问到了myName属性
scala> luck.name
res0: String = Flink
//调用自定义的set方法,修改了myName属性
scala> luck.name = "Spark"
Hi Spark
luck.name: String = Spark
//再次访问myName属性,发现属性值已经变成Spark
scala> luck.name
res2: String = Spark
//修改上面的示例,仅仅暴露属性的get方法,没有为其复写set方法。但是提供一个名为update的方法用来修改该属性的值。
scala> class Person {
| private var myName ="Flink"
| def name = this.myName //get方法
| def update(newName :String){
| myName = newName
| println("Hi "+ myName)
| }
| }
defined class Person
// luck为Person 类的实例
scala> val luck = new Person
luck: Person = Person@7c469c48
//通过自定义的get方法,访问myName属性值,初始值为Flink
scala> luck.name
res4: String = Flink
//想直接通过set方法来修改myName,会提示出错,因为没有复写set方法。
scala> luck.name="Hadoop"
<console>:9: error: value name_= is not a member of Person
luck.name="Hadoop"
^
//通过额外提供的update方法来修改属性
scala> luck.update("Hadoop")
Hi Hadoop
//再次查看myName属性值,发现已经变成了Hadoop
scala> luck.name
res6: String = Hadoop
private[this]private[this]至关重要的,在Spark源码中随处可见。代表属性或方法为对象私有!在类私有的基础上更强一层的控制。
//定义一个类:Person
scala> class Person {
| private var myName ="Flink"
| def name = this.myName
| def update(newName :String){
| myName = newName
| println("Hi "+ myName)
| }
| def talk(p:Person) = {
| println("hello:"+p.name)
| }
| }
defined class Person
//定义 p1为Person对象
scala> val p1=new Person
p1: Person = Person@14555e0a
//定义 p2为Person对象
scala> val p2=new Person
p2: Person = Person@1b2abca6
//把p1的myName 属性值改为p1
scala> p1.update("p1")
Hi p1
//把p2的myName 属性值改为p2
scala> p2.update("p2")
Hi p2
//查看p1的myName 属性值,此时已经改为p1
scala> p1.name
res14: String = p1
//查看p2的myName 属性值,此时已经改为p2
scala> p2.name
res15: String = p2
//调用p1的talk方法,传入p2对象,打印出p2的myName 属性值
scala> p1.talk(p2)
hello:p2
//我们修改一下代码,看看下面代码:
scala>class Person {
| private[this]var name = "Flink"
| def update(newName : String){
| name = newName
| println("Hi " + name)
| }
| def talk(p:Person) = {
| println("hello:"+p.name)
| }
| }
<console>:15:error: valuename is not a member of Person
println("hello:"+p.name)
^
//此时,在定义类时就报错!因为talk方法中p参数是Person类的对象,而name属性被限制为private[this],所以只能在Person类内部使用,Person类的对象无权使用。也就是说在Person类中,update方法中可以使用name,但是talk方法访问对象p的name是不允许的!
// private[this]改成private之后,下面的写法就能正常定义,代码如下:
scala>class Person {
| private varname = "Flink"
| def update(newName : String){
| name = newName
| println("Hi " + name)
| }
| def talk(p:Person) = {
| println("hello:"+p.name)
| }
| }
defined class Person
scala>
构造器的重载scala> class Person {
| private[this] var name ="Flink"
| private[this] var age = 10
| def update(newName :String){
| name = newName
| println("Hi "+ name)
| }
|
| //重载的构造器,首先调用默认的构造器
| def this(name:String){
| this()
| this.name=name
| }
|
| //重载的构造器,调用上面已经存在的构造器
| def this(name:String, age:Int){
| this(name)
| this.age=age
| }
| }
defined class Person
在Spark源码中,构造器重载非常常见。从图1的源代码中可以看出,SparkContext类重写了有很多构造器,在创建SparkContext实例对象时,传递不同的参数来调用相应的构造器。
图1
在Spark中,与类名放在一起的构造器为默认构造器。默认构造器可以带参数也可以不带参数,在图2所示的源代码SparkContext类中,默认构造器带有一个参数。初始化类成员的工作,都放在默认构造器中完成。在SparkContext类中,creationSite、allowMultipleContexts等变量,就是在SparkContext类的默认构造器中完成。
图2
注:本学习笔记来自DT大数据梦工厂 微信公众号:DT_Spark 每晚8点YY永久直播频道:68917580