spark源码的scala解析

本文深入探讨Spark源码中的Scala特性,包括Scala抽象类与Java的差异,Scala泛型与Java的区别,Scala类、对象、包对象的使用规则,以及Scala中的类型约束、柯里化、隐式转换、密封类、自身类型等概念。同时,文章还介绍了Option、Either在错误处理中的应用,以及Future和Promise的使用,揭示了Scala编程中的高级技巧和设计模式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、scala抽象类和java的有何不同?

在org/apache/spark/util/collection/SortDataFormat.scala中有以下抽象类

private[spark] abstract class SortDataFormat[K, Buffer] {...}   

然后在org/apache/spark/graphx/Edge.scala中,直接调用了xxx = new SortDataFormat[Edge[ED], Array[Edge[ED]]] {...}

为啥可以直接new一个抽象类呢??scala的抽象类和java的确实有区别。scala中不需要像java那样必须继承抽象类得到子类,而是直接new,然后在{}内override所有抽象方法,即给出抽象方法的具体实现即可!!!本质上是创建了匿名子类!!!属于语法糖

参考 https://qa.1r1g.com/sf/ask/683363691/ (scala:抽象类实例化?)

二、scala泛型类、泛型函数和java的有何不同?

没太大区别,主要是符号的不同,从<x,y>变为[x,y]。

比如Map[String,Int]、Graph[Int, ED]就是泛型类。

比如org/apache/spark/graphx/lib/TriangleCount.scala中的

def run[VD: ClassTag, ED: ClassTag](graph: Graph[VD, ED]): Graph[Int, ED] = { 以及

def runPreCanonicalized[VD: ClassTag, ED: ClassTag](graph: Graph[VD, ED]): Graph[Int, ED] = {

都是泛型函数。[x,y]加在方法名后面!!!

三、scala的class和object可以在同一文件?

可以,无论case class、普通class、abstract class,都可以;

一个scala文件可以有多个class和多个object!scala默认就是public(即class前面不加东西,就表示public)对外的是哪个class和哪个object??都是(不像java只能有一个public)。比如SVM.scala就包括SVMModel的class和object,以及SVMWithSGD的class和object

提示:scala文件名也没有要求和某个class或object名字一样!?当然,也不可能一样,因为它可以包含多个class和object,和谁一样呢?这点也和java不同!!!

四、scala抽象类可以继承java接口?

如org/apache/spark/broadcast/Broadcast.scala中的

abstract class Broadcast[T: ClassTag](val id: Long) extends Serializable with Logging {

其中Serializable是java的接口。。其实是混入特质(Serializable with Logging),即java的接口被直接当做scala的特质!!!

五、柯里化和类型约束

org/apache/spark/graphx/Graph.scala中有如下代码:

  def outerJoinVertices[U: ClassTag, VD2: ClassTag](other: RDD[(VertexId, U)])
      (mapFunc: (VertexId, VD, Option[U]) => VD2)(implicit eq: VD =:= VD2 = null)
    : Graph[VD2, ED]

这里涉及到两个知识点:柯里化和 =:=

其中=:=是类型约束,VD =:= VD2表示测试VD是否是VD2

注意:implicit eq: VD =:= VD2 = null 不是outerJoinVertices方法的第三个参数,而是类型证明对象,它证明VD的类型和VD2一样,是恒等函数,表示一个约束,也叫做自动隐式转换(注意:implicit后面只能有一个隐式值,比如这里是eq)

六、传名参数

在scala/Predef.scala中有以下代码:

@inline final def require(requirement: Boolean, message: => Any) {

问:message: => Any用到了啥语法?传名参数。另外,这个require方法类似于assert

传名参数的另一个例子是Option的getOrElse方法:

  @inline final def getOrElse[B >: A](default: => B): B =
    if (isEmpty) default else this.get

default: => B如何解读?default是变量,=> B是类型(具体是函数),而这个函数的输入为空(即省略了()),输出为B !

提示:其中的@inline用于建议编译器对方法做内联,而@noinline不要内联

七、包对象

org/apache/spark/graphx/package.scala中有如下代码:

package object graphx { 

这是一个包对象!!!注意:package.scala事实上是graphx.scala

每个包都可以有一个包对象,即package object XXX
在幕后,包对象被编译为带有静态方法和字段的jvm类,如package.class

包对象的内容(如函数和变量)用于直接被包中定义的类等使用
(见快学scala 7.5节)

八、scala特质能否扩展抽象类

可以,比如scala/annotation/Annotation.scala是abstract class;

而scala/annotation/StaticAnnotation.scala是trait,具体是trait StaticAnnotation extends Annotation;可见特质比接口更强大

九、注解的使用

@param @field @getter @setter @beanGetter @beanSetter这些注解都定义在scala/annotation/meta/中,它们的一个用处是修饰org/apache/spark/annotation/Since.scala (定义在common/tags中),而@Since("1.1.0")可用来修饰class SVMModel等

十、this.type是什么

def setThreshold(threshold: Double): this.type = {
    this.threshold = Some(threshold)
    this
  }//来自SVMModel

以上方法返回值为this.type,它表示当前类或对象的类型??是的,比如SVMModel

不写也许可以,但可能得到父类的类型,导致不能串接,见快学scala 18.1节(所以必须加)

思考:为啥Int和String无法调用.type?比如String.type得到error: identifier expected but 'type' found. 要加上type t=str.type前面的type t=才行!!!可以直接在REPL执行type T=Title.type,但不能直接Title.type !也不能用val或var!

十一、None和Some

scala的None是Option定义的,而非通用的,这点和python不同;

具体是case object None extends Option[Nothing];即None是Option的子类(或者说样例对象)

None和Some都可以给Option赋值。 

提示:final case class Some[+A](@deprecatedName('x, "2.12.0") value: A) extends Option[A] {
即Some也是Option的子类

十二、scala中None、Null、null、Nil、Unit、()、Nothing区别

Nothing是所有类型的子类型。它没有实例。表示不正常的终止,被throw使用。(Nothing看不了源码,无法跳转!!)

Null是所有引用类型的子类型。Null的唯一实例是null。(Null和null也看不了源码,无法跳转!!)可以将null赋值给任何引用,但不能给值类型,比如Int设为null是错误的。

Nil是空列表。和List一起使用。具体是:case object Nil extends List[Nothing]

Unit等价于java的void。且它只有一个值,就是()。Unit没有子类!因为是final!!(但稍有区别,void是无值,Unit是有一个表示“无值”的值。)

思考1:Nothing是所有类型的子类型??那它也是Unit/Null的子类型??是的,不冲突,Nothing也是Int的子类型

思考2:scala有Void/void吗??没有,但可以直接用java.lang.Void !

十三、<:<和类型证明对象

@inline final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse ev(null)
这句如何解读?

1)<:<也是定义在Predef.scala中的sealed abstract class,具体含义是:  

   * An instance of `A <:< B` witnesses that `A` is a subtype of `B`.
   * Requiring an implicit argument of the type `A <:< B` encodes
   * the generalized constraint `A <: B`.

简单说,=:=, <:<, <%< 都是带有隐式值的类;都是类型证明对象!!!

为了检验一个泛型的隐式对象是否存在,可在REPL中调用如下,比如
implicitly[String<:<AnyRef] (得到<:<[String,AnyRef] = <function1>)
implicitly[Null<:<AnyRef] (得到<:<[Null,AnyRef] = <function1>)

=:=也定义在Predef.scala中!!也是一个sealed abstract class!
具体含义是:/** An instance of `A =:= B` witnesses that the types `A` and `B` are equal. 

但似乎<%<不存在于Predef.scala!!!

注意一点:=:=, <:<, <%< 都不是scala语言的特性,而是scala库的特性!!!
(因此Predef.scala不属于scala语言而是scala库!?)

2)另外,this getOrElse ev(null)如何解读?和this.getOrElse(ev(null))等价!!!

十四、类型证明对象和隐式转换(深层含义)

再举一个例子

def firstLast[A,C](it:C)(implicit ev: C<:<Iterable[A]) = (it.head, it.last)

以上代码有两层含义:

1. 在相应的伴生对象中,一定存在一个对象,可以被当做C<:<Iterable[A]的实例,它的存在证明了一个事实,即C是Iterable[A]的子类型(如果不存在,会编译报错!即@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}."))

2. 然后编译器会进行隐式转换,(it.head, it.last)会变为(ev(it).head, ev(it).last)(注意:it是普通的C类型,无法执行it.head和it.last,转换一下才行,因为C是Iterable[A]的子类型)

总结:ev叫做类型证明对象,因为它的存在,证明了C是Iterable[A]的子类型;ev也是一个带有单个参数的函数,因此,才可以调用ev(it)进行隐式转换;ev也是恒等函数,永远返回参数原值。

十五、<:<类的本质

1. sealed abstract class <:<[-From, +To] extends (From => To) with Serializable

2. private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }

3. implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]

关键:<:<相对From是逆变,相对To是协变;apply是带有单个参数的恒等函数!!

引申:From和To可以有如下几种组合:两个都是具体的(如String<:<AnyRef)、
      一个具体一个抽象(如Null <:< A1)、两个都是抽象(如C<:<Iterable[A])

测试:如果你在REPL输入implicitly[Any<:<String],会得到报错:
      error: Cannot prove that Any <:< String. 
      即不能证明Any是String的子类型(当然,因为不存在这样的类型证明对象)

十六、隐式参数的默认值和结合性

在org/apache/spark/graphx/impl/GraphImpl.scala中有:

override def outerJoinVertices[U: ClassTag, VD2: ClassTag]
       (other: RDD[(VertexId, U)])
       (updateF: (VertexId, VD, Option[U]) => VD2)
       (implicit eq: VD =:= VD2 = null): Graph[VD2, ED] = {

问其中的VD =:= VD2 = null如何解读?左结合还是右结合?答案是左结合。

理解方式:先把VD =:= VD2看成整体,比如X, 此时变为implicit eq: X = null,即类型为X的eq的默认值为null,当不存在该对象时取默认值(即VD 和 VD2 不相同时),否则就相当于implicit eq: VD =:= VD2

十七、scala的密封类

sealed abstract class Option[+A] extends Product with Serializable {

以上代码中sealed标识密封类。当你用样例类做模式匹配时,可能想让编译器帮你确认你已经列出了所有可能的选择。就要将样例类的通用超类声明为sealed!密封类的所有子类都必须在与该密封类相同的文件中定义。如果某个类是密封的,那么在编译期所有子类就是可见的,因此编译器可以检查模式语句的完整性。让所有(同一组)样例类都扩展某个密封类或特质是好的做法。

密封类提供了一种约束:不能在类定义的文件之外定义任何新的子类。这样可以防止继承的滥用 !类似final的作用!!(比如class NewList extends List就会得到报错illegal inheritance from sealed class List)另外,List也是sealed,它和Nil、::构成了模式匹配!!Option和Some、None构成了模式匹配!!

注意:样例类(以及sealed abstract class)可以模拟枚举!!即可以代替Enumeration

参考 https://blog.youkuaiyun.com/wangxiaotongfan/article/details/82348923 (scala中的sealed)

十八、scala的自身类型

this : 类型 => 是啥?自身类型。(表示下文this指代该类型,而不是原来的所在类!!)

带有自身类型的特质和带有超类的特质很相似,都能保证混入该特质的类具有特定类型的特性;自身类型更灵活,且能解决特质的循环依赖问题,即两个特质彼此需要;自身类型可以处理结构类型,即this后面给出类必须拥有的方法,而不是类名,即这个特质可以被混入任何带有该方法的类;(当类中定义了类似self: X =>以后,该类实例化或者子类的实现的时候,必须混入X类型,当然X的类型 也可以是定义该类的自身类型。变相继承了X的意思!!!)

参考 https://developer.aliyun.com/article/939702 (scala中的self =>)https://blog.youkuaiyun.com/q322625/article/details/118803936 (Scala 语法——self =>)

注意:自身类型 是特质的一个特性!所以java中没有!!

思考:java接口能不能有这个特性??

问题:self => 是干嘛的??用self代替this,即下文就可以用self了!!(即别名)相当于是this : 类型 => 的特例!

十九、implicit关键字的用法

implicit的两个应用之处:
1. implicit eq: VD =:=VD2 =null
2. implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList

即它可以加在方法前面变量前面!!

二十、implicitConversions的作用

import scala.language.implicitConversions 是干嘛的??导入后,就能使用某些功能了,比如使用implicit。。在使用隐式函数的时候,要导入它,否则编译的时候会有个警告

二十一、scala的object和class在继承方面是否等价?

trait Loader[M <: Saveable] 是一个特质,位于org/apache/spark/mllib/util/modelSaveLoad.scala

而object SVMModel 继承了 Loader[SVMModel],可见scala的object能直接继承特质或抽象类!!

也就是说scala的object和class在继承方面是等价的。一个object可以扩展类以及一个或多个特质,结果是一个扩展了指定类或特质的类的对象(见快学scala 6.3节)

二十二、Seq和List什么关系?

如:protected val validators: Seq[RDD[LabeledPoint] => Boolean] = List()

解析:1. List来自val List = scala.collection.immutable.List,具体是object List extends SeqFactory[List] {

2. SeqFactory具体是abstract class SeqFactory[CC[X] <: Seq[X] with GenericTraversableTemplate[X, CC]] extends GenSeqFactory[CC] with TraversableFactory[CC] {

3. 里面的List类是sealed abstract class List[+A] extends AbstractSeq[A]
   with LinearSeq[A]   with Product  with GenericTraversableTemplate[A, List]
   with LinearSeqOptimized[A, List[A]]  with scala.Serializable {

4. AbstractSeq具体是abstract class AbstractSeq[+A] extends AbstractIterable[A] with Seq[A]

可见List是Seq的子类!!

但是注意:Seq是特质,而List是abstract class!!

细节:trait Seq[+A] extends PartialFunction[Int, A]
                      with Iterable[A]
                      with GenSeq[A]
                      with GenericTraversableTemplate[A, Seq]
                      with SeqLike[A, Seq[A]] {

另外有,trait LinearSeq[+A] extends Seq[A]
                            with scala.collection.LinearSeq[A]
                            with GenericTraversableTemplate[A, LinearSeq]
                            with LinearSeqLike[A, LinearSeq[A]] {

List和Seq的关系细节确实很复杂!!

二十三、+A和A同时出现的含义

比如trait LinearSeq[+A] extends Seq[A]

表示LinearSeq[+A]特质和A类型是协变的!!与后面extends Seq[A]无关!!

或者说,先有trait LinearSeq[A] extends Seq[A],再把+加上去

(如果不加+,默认是啥意思?协变or逆变or都不是?都不是

二十四、是否是返回值

如:override protected def createModel(weights: Vector, intercept: Double) = {
    new SVMModel(weights, intercept)
  }

问:这个方法有没有返回值??new SVMModel 是普通语句(类似print),还是返回值?

答:是返回值,只不过因为知道返回值类型,就在方法上省略了。。

二十五、迷惑的Any

如:trait Serializable extends Any with java.io.Serializable

这里extends Any目的是啥??任何类型不都是Any的子类吗,为啥需要再写extends Any??写了才是??如果写了才是,那用户自定义的类(没有继承Any)不是Any的子类吗?

是否意味着,最基础的自定义特质,并不是Any的子类??只有scala库的才是??

仅仅为了符合Any是一切类型的父类而已!?

在idea中,Any和null一样,跳转不了了,已经是最原子了。。那它是类还是啥?

Any是类,因为它是scala类层级的根节点!!但我在idea中找不到它的实现,而且无法跳转,我以为它是基本类型,但事实上基本类型也是能跳转的,比如Int

听说Any实现了Nothing特质,且定义了isInstanceof和asInstanceof方法??

abstract class Any {
  def equals(that: Any): Boolean  //值比较
  def hashCode(): Int  //hash值
  def toString(): String  
  final def getClass(): Class[_] = sys.error("getClass")  
  final def ==(that: Any): Boolean = this equals that  // 值比较,支持 null
  final def != (that: Any): Boolean = !(this == that)  // 值比较
  final def ## : Int = sys.error("##")  // hash值,支持 null
  final def isInstanceOf[T0]: Boolean = sys.error("isInstanceOf") //是否为 T0 实例
  final def asInstanceOf[T0]: T0 = sys.error("asInstanceOf") //强转为 T0
}

这是网上找的一个版本。。确实定义了isInstanceof和asInstanceof。。但没有实现Nothing特质。。另一问题是,Any和Object关系怎么看?似乎没有关系。。

(参考 http://t.zoukankan.com/wudeyun-p-12865294.html)

scala 让编程者感觉 Any类是scala的顶级父类;作为jvm来说,Object才是顶级父类,scala编译器必然将Any、AnyRef编译为 Object 的子类型,这是scala编译器来实现的,编程者完全无感(在scala范围,Any确实是顶级父类;但在jvm范围,Object才是顶级父类)

AnyRef也是最原子,跳转不了的!!但AnyVal不是,具体是abstract class AnyVal extends Any

二十六、Int的解析

final abstract class Int private extends AnyVal {

问1:Int是抽象类,既然抽象为啥可以加final??只能实例化,不能被继承!!(RichInt就是非抽象的类,即final class RichInt)

问2:这里的private是干嘛的?修饰主构造器的??还是私有继承??应该是修饰类的,表示包内使用

引申:class Person private(val id:Int)的private修饰的是主构造器,把它变为私有,
即只能通过辅助构造器来构造Person对象。

思考:private和private[xxx]永远不会出现在class前面??不是,比如private[spark] case class Instance(label: Double, weight: Double, features: Vector)
(在org/apache/spark/ml/feature/Instance.scala)

问题:修饰class的话,private[xxx]到底应该放前面还是后面???

总结:private和private[xxx]可以加在字段或方法前面,也可以加类后面!!protected和protected[xxx]也一样!!

二十七、object能否嵌套定义object?

object NaiveBayesModel extends Loader[NaiveBayesModel] {
  import org.apache.spark.mllib.util.Loader._
  private[mllib] object SaveLoadV2_0 {

可以,如上。另外,object中也可以定义class或case class!!

二十八、特质是一种类型吗?

已知scala没有多继承,如果是,那extends ... with多个特质,和真正的多继承,有啥区别?

但是既然特质可以extends类,那为啥不是类型?

跟scala的macro有关?特质不是类型,特质的type是类型!?

二十九、实现多个特质和继承多个类,有何区别?

1. extends A with B with C 这里BC一定是特质,而A可以是特质或类?

2. 当A是类时,是单继承,当A是特质时,三个都是特质,所以不存在继承,能这么说?

3. extends A with B with C 如果ABC都是特质,这个能叫继承吗,还是只能叫混入(mix in)?也可以叫吧,见快学scala的10.2节(extends(A with B with C)),这样的话,父类是整体还是A?

三十、混入、实现/继承的区别

extends特质叫混入特质还是实现/继承特质?混入和继承啥区别?使用混入是为了避免多继承?

三十一、scala有多继承之实?

scala虽然没有多继承之名,但有多继承之实?

似乎也不是。。

虽然利用scala允许特质扩展类,此时混入多个特质,就相当于有多个超类了。。

但这种用法是受限的,要考虑实际情况!!

当已经有超类时,不能用该用法混入特质(从而引入超类2),除非超类1和超类2是继承关系!

三十二、scala值类型

scala允许扩展AnyVal的用户定义的值类;值类不分配运行时对象;值类允许将扩展方法添加到类型,而不需要创建实例的运行时开销,比如:

class SomeClass(val i: Int) extends AnyVal {
    def twice() = i*2
}

注意:值类的主要目的是通过避免运行时分配来优化程序。值类不能被其他类继承。值类不能嵌套。它只能有方法( def )作为其成员,即不允许任何变量 。

参考 https://blog.youkuaiyun.com/cumtb2002/article/details/107790458 (Scala中的值类)

三十三、scala的类型类

类型类是一种类似继承的关系。(scala typeclass is elegant !!)

TypeClass 是 Haskell 语言里的概念,根据《Learn you a haskell》中的解释,TypeClass 是定义一些公共行为的接口,Java 语言中最为接近的概念是 interface,Scala 语言中最为接近的概念是 trait.

TypeClass将行为定义具有行为的对象分离,更容易实现 DuckType;同时, 在函数式编程中,通常将数据与行为相分离,甚至是数据与行为按需绑定,已达到更为高级的组合特性。

Scala 中实现 TypeClass 可总结为三板斧
1. TypeClass Trait 定义
2. 定义 TypeClass 隐式实例
3. 定义 TypeClass 语法结构

@typeclass是scala提供的注解??是Simulacrum项目的,需要import simulacrum._

@typeclass trait Semigroup[A] {
  @op("|+|") def combine(a1: A, a2: A): A
}

以上就能轻松实现类型类!!且用|+|代替combine方法。减少了很多样板代码。

群友:scala的特质比抽象类多承载了一个typeclass的功能!!

参考 https://www.jianshu.com/p/52450e251c84 (Scala 中 Type Class 实现的套路)
https://blog.youkuaiyun.com/sparrowxin/article/details/109825956 (Scala Cats - Type Class 类型类)
https://zhuanlan.zhihu.com/p/35179998 (Scala with Cats 入坑指南)

三十四、Scala3的类型类

Type classes 源自 Haskell,在 Scala 中并没有直接的语法和概念,但却可以借助于强大的隐式系统间接实现,一般称之为 Type classes Pattern

在Scala2中,隐式系统是造成Type classes Pattern难以掌握的原因之一。而在Scala3中,将再也不用为纠结 implicit 该怎么用了,它被全新的 Given、Using 、Extension Method 等特性所替代。

参考 https://zhuanlan.zhihu.com/p/164578228 (真的学不动了:Scala3 与 Type classes)

Type classes 结合了 ad-hoc polymorphism(特设多态)和 Parametric polymorphism (参数化多态),实现了一种更通用的重载,比如算术重载。

其中ad-hoc polymorphism(特设多态)指的是函数应用不同类型的参数时,会有不同的行为(或者说实现)。Parametric polymorphism(参数化多态)指的是函数被定义在某一些类型之上,
对于这些类型来说函数的实现都是一样的,比如 List[T] 的 size() 函数。虽然 Type classes 结合了两种多态类型,但它本身却被归到特设多态(ad-hoc polymorphism)这一分类下。

目前在 Java 中是无法实现 Type classes 的,但同为 JVM 的语言,多范式的 Scala 却可以实现。但 Type classes 在 Scala 中其实也不是一等公民,也就是没有直接的语法支持,但借助于强大的隐式系统我们也能实现 Type classes,由于实现的步骤比较公式化,也就被称之为 Type classes Pattern (类型类模式)。

在 Scala 中,类型类的设计其实随处可见,典型的就有 Ordered (所以类型类是一种设计模型??)。在 Scala3 中,Type classes 得到了足够的重视,直接提供了语法层面的支持
再也不用写一大堆的模板代码, 从此可以叫做 Type classes without Pattern

参考 https://blog.cc1234.cc/posts/typeclasses-1/ (真的学不动了:除了 class , 也该了解 Type classes 了)

三十五、类型类案例之Ordered

object Ordered {
  /** Lens from `Ordering[T]` to `Ordered[T]` */
  implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
    new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
}

以上是Ordered的object,关键是implicit ord这个隐式对象,依赖它的ord.compare方法

trait Ordered[A] extends Any with java.lang.Comparable[A] {...}是Ordered的特质

三十六、scala类型查看、判断、转换

y.getClass
y.getClass.getName
y.getClass.getType
y.getClass.getTypeName
y.getClass.getSimpleName

classOf[List[Int]] (里面是类,而不是对象!!)
classOf[List[Int]] == classOf[List[String]] (结果为true)

typeOf[List[Int]] (里面也是类,而不是对象!!)
typeOf[List[Int]]==typeOf[List[String]] (结果为false)

注意:typeOf需要导入额外包,即import scala.reflect.runtime.universe.typeOf !

结论:classOf是按照去掉泛型后的(即List)来判断的;而typeOf是更细的按照未去泛型(即List[Int]和List[String])来判断的。

classOf[T] 相当于 java中的T.class !!!
y.getClass ==classOf[Double] !!! (其中y是Double类型的数值)

参考 https://blog.youkuaiyun.com/hellojoy/article/details/81020832 (scala 中Type与Class初步学习)

scala判断类型: p.isInstanceOf[Int]

scala转换类型: val y=p.asInstanceOf[Double]

三十七、方法中??? 的含义

方法后面是??? 表示啥?比如

final abstract class Unit private extends AnyVal {
  // Provide a more specific return type for Scaladoc
  override def getClass(): Class[Unit] = ???
}

其中Unit.getClass得到Class[_ <: Unit.type] = class scala.Unit$

???是一个方法,定义在Predf.scala中,具体是:
def ??? : Nothing = throw new NotImplementedError
(即没有任何实现,只是抛出异常或错误)

含义是`???` can be used for marking methods that remain to be implemented.

三十八、scala枚举

与Java不同,Scala中没有枚举类型,需要我们通过标准库类 Enumeration 来实现,比如
object BusinessType extends Enumeration{
  var FLIGHT, HOTEL, TRAIN, COACH = Value
}

然后如下使用
val FLIGHT = Value(0, "FLIGHT")
val HOTEL = Value(10) // 名称为"HOTEL"
val TRAIN = Value("TRAIN") // ID为11

参考 ​https://cloud.tencent.com/developer/article/1481725​

三十九、scala隐式引入的包

import java.lang._
import scala._
import Predef._

以上都是隐式引入的!!
注意:scala.StringBuilder会覆盖java.lang.StringBuilder而不是与之冲突!!
这一点是和其他引入不同的!!

四十、scala反射

包括import scala.reflect.runtime、scala.reflect.ClassTag、scala.reflect.ScalaSignature、scala.reflect.macros.Typers|Names|Aliases、scala.reflect.api.Types|Annotations|Symbols等

四十一、ClassTag特质

trait ClassTag[T] extends ClassManifestDeprecatedApis[T] with Equals with Serializable {

  @transient private[scala] lazy val emptyArray       : Array[T]                = {
    val componentType =
      if (runtimeClass eq java.lang.Void.TYPE) classOf[BoxedUnit] else runtimeClass
    java.lang.reflect.Array.newInstance(componentType, 0).asInstanceOf[Array[T]]
  }

1. @transient是干嘛的?

@transient来自scala/transient.scala,具体是class transient extends scala.annotation. StaticAnnotation;具体作用是,将字段标识为瞬态的,不会被序列化,对于临时保存的缓存数据很有用。

2. runtimeClass eq java.lang.Void.TYPE啥意思?

等价于runtimeClass.eq(java.lang.Void.TYPE);scala的eq和ne类似java的==和!=,即判断引用相等性。

四十二、Equals特质

即trait Equals extends Any {...}

方法有def canEqual(that: Any): Boolean 和 def equals(that: Any): Boolean

四十三、ClassManifestDeprecatedApis特质

来自scala/reflect/ClassManifestDeprecatedApis.scala,已经deprecated,不用深入研究;直接用scala.reflect.ClassTag即可!

四十四、scala的存在类型

private[ml] trait ClassifierTypeTrait {
  // scalastyle:off structural.type
  type ClassifierType = Classifier[F, E, M] forSome {
    type F
    type M <: ClassificationModel[F, M]
    type E <: Classifier[F, E, M]
  }
  // scalastyle:on structural.type
}//来自spark\ml\classification\OneVsRest.scala

forSome{}表示存在类型,其中{}中包含type和val的声明;存在类型加入scala是为了与java类型通配符兼容;类型通配符是存在类型的语法糖;forSome表达能力更强

Array[_]等价于Array[T]forSome{type T}
Map[_,_]等价于Map[T,U]forSome{type T;type U}
更复杂的例子:Map[T,U]forSome{type T;type U<:T}

使用val的例子:n.Member forSome{val n:Network} 等价于类型投影 NetWork#Member !!!

forSome比类型投影功能更强,比如要接受相同网络的成员而拒绝不同的
(见快学scala 18.8节)

思考:类型投影 NetWork#Member 和普通内部类啥区别?

四十五、scala的$符号

final def getFeaturesCol: String = $(featuresCol)

如上,这里的$什么意思?其实是spark定义的方法,不是scala本身的。具体是: 

  /**
   * An alias for `getOrDefault()`.
   */
  protected final def $[T](param: Param[T]): T = getOrDefault(param)

参数必须是spark ml的class Param[T]类型,而getOrDefault也是ml定义的。

但其实scala本身也有$操作,一个例子:

private[sql] def sql = s"${quoteIdentifier(name)}: ${dataType.sql}$getDDLComment"
(来自org/apache/spark/sql/types/StructField.scala)

其实就是引用作用域内的变量的值!

四十六、try block finally语法

private[sql] def withActive[T](block: => T): T = {
    val old = SparkSession.activeThreadSession.get()
    SparkSession.setActiveSession(this)
    try block finally {
      SparkSession.setActiveSession(old)
    }//来自SparkSession.scala
  }

try block finally 表示只有try 和finally,没有catch。try的对象是block

参考 https://blog.youkuaiyun.com/baguashenp74070/article/details/101893157 (Scala Try Catch Finally)

四十七、Try、Failure、Success和NonFatal

Try具体是sealed abstract class Try[+T] extends Product with Serializable {...}(来自scala/util/Try.scala),是一个密封类

它的子类分别是Success、Failure。具体是final case class Success[+T](value: T) extends Try[T]和final case class Failure[+T](exception: Throwable) extends Try[T]

Try和Success、Failure、NonFatal一起使用。一个例子:

object Try {
  /** Constructs a `Try` using the by-name parameter.  This
   * method will ensure any non-fatal exception is caught and a
   * `Failure` object is returned.
   */
  def apply[T](r: => T): Try[T] =
    try Success(r) catch {
      case NonFatal(e) => Failure(e)
    }
}

其中NonFatal来自scala.util.control.NonFatal,是一个object(即object NonFatal {...})

case NonFatal(ex) => ...使用NonFatal来匹配所有的非致命性异常(像内存泄漏之类的就是致命异常)。

参考 https://zhuanlan.zhihu.com/p/54218541 (Scala Try 与 try-catch-finally)
https://blog.codacy.com/why-use-try-instead-of-scala-try-catch/

四十八、String* 以及:_* 啥意思?

  @scala.annotation.varargs
  def sort(sortCol: String, sortCols: String*): Dataset[T] = {
    sort((sortCol +: sortCols).map(Column(_)) : _*)
  }//来自Dataset.scala

String* 是可变参数(variable arguments);比如REPL输入def concat(strs: String*): String = strs.foldLeft("")(_ ++ _)和concat("foo", " ", "bar") ,得到String = foo bar

:_*作用是将你的集合变成一个varargs,而不是一个参数传进去,即一变多!!

:_*作为一个整体,告诉编译器你希望将某个参数当作参数序列处理!

参考 https://blog.youkuaiyun.com/Elimeny/article/details/104048822
https://www.cnblogs.com/shichunlei/p/12928375.html

四十九、Either、Left和Right

处理异常的时候Option会有很大的限制,Option返回None,并不知道具体的异常到底是什么,因此scala引入了Either;Either表示或者是这个元素或者是那个元素。

A common use of `Either` is as an alternative to [[scala.Option]] for dealing
 *  with possibly missing values.
[[scala.None]] is replaced
 *  with a [[scala.util.Left]] which can contain useful information.
 *  [[scala.util.Right]] takes the place of [[scala.Some]].
即Left表示失败,Right表示成功。

Either来自/scala/util/Either.scala,具体是sealed abstract class Either[+A, +B] extends Product with Serializable;两个重点:

1. you could use `Either[String, Int]` to indicate whether a received input is a `String` or an `Int`.

2. `Either` is right-biased, which means that `Right` is assumed to be the default case to operate on.

两个子类Left和Right具体是final case class Left[+A, +B](@deprecatedName('a, "2.12.0") value: A) extends Either[A, B] {...}和final case class Right[+A, +B](@deprecatedName('b, "2.12.0") value: B) extends Either[A, B] {...}

Either封装好了异常,不会影响程序的正常运行,而且可以返回具体的错误信息,实在是一个不错的设计方式。

参考 https://zhuanlan.zhihu.com/p/137365275 (Scala教程之-Either)
https://blog.youkuaiyun.com/cusi77914/article/details/107112279(scala either_使用Either和Option处理功能错误)

五十、Future特质

Future特质来自scala/concurrent/Future.scala,具体如下trait Future[+T] extends Awaitable[T] {...}

方法有onSuccess、onFailure、onComplete、isCompleted、value、failed、foreach、
transform、transformWith、map、flatMap、flatten、filter、withFilter、collect、
recover、recoverWith、zip、zipWith、fallbackTo、mapTo、andThen

其中onSuccess和onFailure依赖PartialFunction(偏函数)作为参数,且依赖ExecutionContext作为隐式参数

Future也提供了伴生对象,并内部定义了final object never extends 以及unit字段、failed方法、successful方法、fromTry方法、apply方法、sequence方法、firstCompletedOf方法、find方法、foldLeft方法、fold方法、reduce方法、reduceLeft方法等

具体用法如下

/** A `Future` represents a value which may or may not *currently* be available,
 *  but will be available at some point, or an exception if that value could not be made available.
    val f = Future { 5 }
    val g = Future { 3 }
    val h = for {
      x: Int <- f // returns Future(5)
      y: Int <- g // returns Future(3)
    }yield x + y

等价于val h=f flatMap { (x: Int) => g map { (y: Int) => x + y } }

群友:for yield只有最后一行会对应到map,其他会对应到flatMap !!而if会对应到withfilter

扩展:在scala中是没有原生线程的,其底层使用的是java的Thread机制。在scala中对java Thread进行了封装,实现了更便于操作线程的Future。

参考 http://www.javashuo.com/article/p-yyuuland-dk.html (scala(二) Future执行逻辑解读)

五十一、Promise特质

Promise是特质,方法有future、isCompleted、complete、tryComplete、completeWith、
tryCompleteWith、success、trySuccess、failure、tryFailure

Promise也提供了object,方法有apply、failed、successful、fromTry

KeptPromise提供了Promise的一些具体实现,位于scala/concurrent/impl/Promise.scala,
其中sealed trait Kept继承Promise,而final class Successful继承Kept,final class Failed继承Kept(注意:KeptPromise处于object Promise之中)

impl/Promise.scala也定义了一个Promise特质,具体是 private[concurrent] trait  Promise[T]  extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {...} 方法有transform、transformWith、toString

impl/Promise.scala还定义了DefaultPromise,也是Promise的具体实现
class DefaultPromise[T] extends AtomicReference[AnyRef](Nil) with Promise[T] {...}

五十二、Promise和Future的关系

Promise特质封装了Future,表示promise的值

 * Promise is an object which can be completed with a value or failed
 *  with an exception.

但另一个Promise,即impl/Promise.scala的Promise特质却是扩展了Future和第一个Promise !如下:private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] {...}

而DefaultPromise类又混入了第二个Promise,所以它是具体的Future实现类!!因此,经常出现Future实例调用getClass得到scala.concurrent.impl.Promise$DefaultPromise

五十三、scala的@操作符

  def apply[T](result: Try[T]): scala.concurrent.Promise[T] =
      resolveTry(result) match {
        case s @ Success(_) => new Successful(s)
        case f @ Failure(_) => new Failed(f)
      }//来自impl/Promise.scala

这里s @ Success的@是绑定的意思,也是另类的赋值,而不是判断类型!!

比如既想拿到b:B又想拿到unapply出来的东西,就这么搞,例如:

case b @ B(name:String)=>{
   val c: B=b
   val d: String=name
}

五十四、scala的CanBuildFrom特质

CanBuildFrom来自scala/collection/generic/CanBuildFrom.scala

它是一个特质,具体是trait CanBuildFrom[-From, -Elem, +To]

它有@implicitNotFound注解,会提示无法从From变为To,"Cannot construct a collection of type ${To} with elements of type ${Elem} based on a collection of type ${From}."

它是一个 base trait for builder factories;方法只有两个apply方法,返回Builder[Elem, To];其中Builder也是特质,即trait Builder[-Elem, +To] extends Growable[Elem] {...}

用处:CanBuildFrom被用在Future的sequence、traverse方法的隐式参数中;还有List、Map、Vector的伴生对象中;另外在spark的org/apache/spark/storage/BlockManagerMaster.scala也出现了一次(具体是getBlockStatus方法)

五十五、scala的case可以没有match?

可以!!可以是偏函数!!!比如scala/concurrent/Future.scala的transform方法

def transform[S](s: T => S, f: Throwable => Throwable)(implicit executor: ExecutionContext):       Future[S] =
    transform {
      case Success(r) => Try(s(r))
      case Failure(t) => Try(throw f(t)) // will throw fatal errors!
    }//里面的transform返回Future类型

Try中有一个collect方法,它的参数是偏函数pf: PartialFunction[T, U]
另外Future也有一个collect方法,它的参数也是偏函数pf: PartialFunction[T, S]

具体是怎么个调用机制,搞不懂。。。

五十六、scala的PartialFunction

PartialFunction即偏函数,来自scala/PartialFunction.scala,描述如下

/** A partial function of type `PartialFunction[A, B]` is a unary function
 *  where the domain does not necessarily include all values of type `A`.
 *  The function `isDefinedAt` allows to test dynamically if a value is in
 *  the domain of the function.
一个例子
 *  val sample = 1 to 10
 *  val isEven: PartialFunction[Int, String] = {
 *    case x if x % 2 == 0 => x+" is even"
 *  }

PartialFunction是个特质,即trait PartialFunction[-A, +B] extends (A => B) { self =>...

方法有isDefinedAt、orElse、andThen、lift、applyOrElse、runWith等

PartialFunction也提供了object;伴生对象内部定义了private class OrElse、private class AndThen、private class Lifted、private class Unlifted等类,以及empty、cond、condOpt方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值