二、Scala中的Object实战详解 |
object
定义一个类同名的object对象,里面存放静态的成员或者方法,把该object对象称之为该类的伴生对象。
图3
在图3中,SparkContextobject为SparkContext类的伴生对象,里面存放一系列静态的成员和静态的方法。而且,当我们第一次调用SparkContext时,SparkContext伴生对象会被执行一次,仅此一次。下面是一个示例:
图4
图4中object Person是class Person的伴生对象,class Person称之为object Person的伴生类。伴生对象适合在里面定义一些工具方法,以及存放一些全局唯一的常量,节省空间。
//第一次调用:Person伴生对象的getSalary方法
//此时,我们发现:Person伴生对象被初始化,打印出字符串Scala。
scala> Person.getSalary
Scala
res34: Double = 0.0
//第二次调用:Person伴生对象的getSalary方法
//此时,我们发现:Person伴生对象不再需要初始化,没有打印出字符串Scala。
scala> Person.getSalary
res35: Double = 0.0
以上示例中确实验证了:伴生对象会被执行一次,仅此一次。
Scala中,我们在定义类的对象时, 一般都不会用new类名,再传入参数的方式来定义。而是直接用类名,或者类名加参数的方式。例如,我们重温下面的示例:
我们构造一个数组,其实是调用Array的伴生对象object Array的apply方法。
scala> val array=Array(1,2,3)
array: Array[Int] = Array(1, 2, 3)
scala> val array=Array.apply(1,2,3)
array: Array[Int] = Array(1, 2, 3)
以上两种定义数组的方式效果是一样的。apply方法就是当前类的伴生对象的工厂方法。延伸一下,Java水平比较高的编程人员,在构造Java对象时,一般来说不会直接new一个类,而是通过工厂方法模式来创建。而Scala语言中,天生就支持这次模式,所以在具体类对象构造时,一般都是在伴生类的伴生对象的apply方法中去实现!这样可以控制对象的生成。
三、Scala中的抽象类、接口实战详解
abstract
抽象类使用abstract关键字,以Spark源码RDD类为示例,如下图5所示:
图5
子类去继承抽象类,跟Java一样,也是使用extends关键字。如JdbcRDD就继承了RDD抽象类,如下:
- classJdbcRDD[T: ClassTag](
- sc: SparkContext,
- getConnection: () => Connection,
- sql: String,
- lowerBound: Long,
- upperBound: Long,
- numPartitions: Int,
- mapRow: (ResultSet) => T = JdbcRDD.resultSetToObjectArray _)
- extends RDD[T](sc,Nil) with Logging {
那么JdbcRDD就可以使用父类RDD所有它可以使用的属性和方法。当然,子类也可以覆盖父类的属性和方法。注意,这里面有个前提,父类的属性和方法没有加final。
一个抽象类,那么里面肯定是定义了某个方法,但是没有又实现体,只有方法的说明。比如:
图6
图6代码块中,compute就是RDD抽象类的一个方法,只有说明,没有实现体。
override
在上面JdbcRDD子类中,因为JdbcRDD继承了RDD抽象类,那么在JdbcRDD中一定要复写compute方法。如图7所示:
图7
使用override关键字表示复写父类的该方法。除了覆盖父类的方法,也可以使用override来覆盖父类的val属性。父类在定义属性时没有给具体的值,就是抽象属性,在子类中,子类必须覆盖该属性。
trait
相当于Java的接口。在trait定义的类中可以定义抽象方法,但是又没有具体的实现。子类可以使用extends关键字来继承该父类。
以Spark源码中的RDD类为示例,RDD继承了Serializable和Logging两个父类。一个子类如果继承多个父类,那么继承的第一个父类前使用extends关键字,后面的父类使用with关键字。RDD继承的这两个父类,在定义时都使用trait关键字来定义。Scala语法中,不允许一个子类同时继承多个抽象父类,但是允许同时继承多个trait父类。
- abstract class RDD[T: ClassTag](
- @transient private var _sc: SparkContext,
- @transient private var deps: Seq[Dependency[_]]
- ) extends Serializable with Logging {
图8是Serializable类的源码:
图8
trait的用途,更多的是作为工具方法的容器。我们把通用的功能,放在trait中。一个子类,如果需要把很多通用的功能都混进来,那么就需要继承多个trait父类,继承的第一个父类使用extends,后面的父类使用with。Spark源码Master类就是一个示例,继承了多个父类。如图9所示:
图9