一、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方法