记录一些新学到的scala知识,偶尔更新。
1、构造方法的问题
class SparkSession private(
@transient val sparkContext: SparkContext,
@transient private val existingSharedState: Option[SharedState],
@transient private val parentSessionState: Option[SessionState],
@transient private[sql] val extensions: SparkSessionExtensions)
extends Serializable with Closeable with Logging { self =>
private() 表示构造方法私有化
Scala比Java更面向对象的一个方面是Scala没有静态成员。替代品是,Scala有单例对象:singleton object。 当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
private[sql] def this(sc: SparkContext) {
this(sc, None, None,
SparkSession.applyExtensions(
sc.getConf.get(StaticSQLConf.SPARK_SESSION_EXTENSIONS).getOrElse(Seq.empty),
new SparkSessionExtensions))
}
这个是辅助构造方法,private[sql]表示包私有,需要在一个包内才能使用
1.1、表示有伴生类和伴生对象
2、伴生对象(单例对象)
单例设计模式
伴生类
可以实现全局静态属性或方法
单例对象的属性和方法都可以由伴生类直接调用
Scala比Java更面向对象的一个方面是Scala没有静态成员(无Static)。替代品是,Scala有单例对象:singleton object。
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
package com.test.scala.scalaTest
object TestObject {
def main(args: Array[String]): Unit = {
//val student=new Student("zhangsan",13)
//student.printInfo()
Student.newStudent("zhangasn",13).printInfo()
//apply调用
val student1=Student("adwa",11)
student1.printInfo()
}
}
//val 既是主构造器的参数,也是属性
//private 构造方法私有化
class Student private(val name:String,val age:Int){
def printInfo(): Unit ={
println(s"name=${name},age=${age},School=${Student.School}")//这个Student是伴生对象
}
}
//伴生对象
object Student{
val School:String="nanhu"
//定义一个类的对象实例创建方法,无需new
def newStudent(name:String,age:Int): Student=new Student(name,age)
//调用apply的时候,有优惠政策,可以直接省略掉
def apply(name:String,age:Int): Student=new Student(name,age)
}
3、require 测试,参数检查
@inline final def require(requirement: Boolean, message: => Any) {
if (!requirement)
throw new IllegalArgumentException("requirement failed: "+ message)
}
4、try isSuccess
调用isSuccess来检查一个try是否成功
5、集合、数组就有可能出现 空指针异常
6、运算符的本质
和java本质上的不同
运算符当作对象的方法调用
如:
val n1: Int = 12
n1看作对象
n1+n2
//等同于
n1.+(n2)
//调同了n1的+方法
真正面向对象的思路!
属于合成方法,可以绕过JVM,sdk中找不到实现
7、if
if有返回值,值是if中的最后一条语句的值
不同分支,返回值类型不同,用公共父类类型的值来接收。如Any
val x:Any=if(x>0){
"小王"
}else(x<0){
11
}
//java中的三元运算符: a?b:c
val res:String=if(x>11){
"1111"
}else{
"2222"
}
//缩写成
val res=if(x>11) "1111" else "2222"
8、for 非常适合集合的转换
//不包含10,to的底层是实现
for(i <- Range(1,10)){}
//更好的方法,until的底层就是Range
for(i <- 1 until 10){}
//遍历集合,数组
for(i <- Set(12,23,44)){}
for(i <- Array(1,2,3,4))
循环守卫
作用相当于java中的 continue
for(i <- 1 to 10 if i != 5){}
循环引入变量
for{
i <- 1 to 10
j=10 -i
}{
循环体
}
生成集合类型
yield关键字:保存当前元素,生成集合
val b: immutable.IndexedSeq[Int] = for(i <- 1 to 10) yield i * 2
实现类似java中的break操作
try{
for(i <- 1 to 10){
if(i == 5)
throw new RuntimeException
println(i)
}
}catch{
case e: Exception => //do nothing
}
//更好的方法 使用Scala中的Breaks类的break方法,实现异常的抛出和捕捉
Breaks.breakable(
for(i <- 0 util 5){
Breaks.break()
println(i)
}
)
9、While
变量必须在外部定义,不适合处理集合
10、函数式编程 & 面向对象、面向过程
都是scala的特点
面向过程:效率高
面向对象:可维护性强,更重要
都是命令式编程,服务于硬件,每一行代码都能翻译成机器指令
函数式编程:函数指的是数学方面的函数,对人更好理解
都使用val
都有返回值
不停 y=f(x) 来转换
得到的结果是确定的,适合分布式,提高并行处理能力,如RDD
11、函数 & 方法
java一般都说方法,因为方法是指通过对象才能调用的,不能独立调用
scala讲函数,可以不依赖于对象
函数概念:
思想来源:y=f(x)
-
为完成某一功能的程序语句的集合,称为函数。
-
类中的函数称为方法。
scala中的函数可以随处定义,可以在函数中定义函数
函数参数
-
可变参数,多个参数时可变参数放最后
//可以接收数组
def f(str: String*): Unit={}
-
带默认值
def f(str: String = "awdaw"): Unit={}
-
带名参数 (有默认值的时候适合使用)
f(age=21)
12、Lamda表达式
即 匿名函数,(x:Int)=> {函数体}
可以以函数作为参数
val fun: String => Unit = (name: String) => println(name)
fun("dad")
def f(func:String=>Unit): Unit ={
func("wad")
}
f(fun)
//更好的做法
f((name: String) => println(name))
简化:
(1)参数类型可以省略
(2)只有一个参数,圆括号可以省略
(3)如函数体只有一行,大括号{}可以省略
(4)如果每个参数只出现一次,参数省略,=>省略,且后面参数可以用 _表示
(5)如果可以推断出,当前传入的println是函数体,而非调用语句,可以省略下划线
例:
arr.foreach(println)
函数类型 如 String => Unit
一般的函数传入的都是数,这个可以实现把操作传入函数
def f(fun:(Int,Int)=>Int):Int={
fun(1,2)
}
val add=(x:Int,y:Int) => x+y
println(f(add))
//简化
f((x:Int,y:Int) => x+y
f((x,y) => x+y)
f(_+_)
13、高阶函数
-
函数可以作为值传递 =
-
函数可以作为参数传递
-
函数可以作为返回值传递
//表示函数整体
val f1=f(_)
//或者
val f1=f _
14、mkString
用于把集合/数组里的元素转化为字段串
可以在前后以及间隔添加任意字符
val a=Array("apple","banana","orange")
val b=Seq("dawd","dad")
println(a.mkString("[",",","]"))
println(b.mkString(","))
15、type关键字,<:
type相当于声明一个类型别名:
//String类型用S代替
type S = String
泛型:
上边界:<: 前者必须是后者的子类
下边界:>: 后者是前者的子类
16、惰性赋值:lazy
用lazy定义的变量只有在使用时才会执行初始化。
好处:
-
打开一个数据库连接。这对于程序来说,执行该操作,代价式昂贵的,所以我们一般希望只有在使用其的引用时才初始化。(当然实际开发中用的是连接池技术)
-
为了缩短模块启动时间,可以将当前不需要的某些工作推迟执行。
-
保证对象中其他字段的初始化能优先执行
17、Java序列化 /@transient
@transient是类型修饰符,只能用来修饰字段。在对象序列化的过程中,@transient标记的变量不会被序列化。
Scala序列化继承自Java
序列化的前提:
-
类要继承Serializable
-
类中所有字段都必须是可序列化的
18、map映射操作
函数式编程的特点,map是一个高阶函数,可以接收函数作为参数
def add(n:Int):Int = {
n++
}
val list2 = list1.map(add)
说明:map
-
将集合遍历
-
将各个元素值传给函数
-
将得到新值放入到一个新的集合并返回
19、:+ / +:
:+方法用于在集合或数组尾部添加元素
+:方法用于在头部添加元素
20、ClassTag
使用范型时,只要方法体或函数体内包含了T,K,V此类泛型标识,必然会有类型擦除
-
使用[T:ClassTage]即可,import
def buildArray[T:ClassTag](a1:T, a2:T): Array[T] ={
val array:Array[T]=Array(a1,a2)
array
}
-
Manifest也可,无需import
21、隐式转换 implicit
-
隐式转换的核心是from类型和to类型
-
隐式转换只关心
类型
使用方法:
-
隐式值:用于给方法提供参数
implicit val p = "mobin"
def person(implicit name : String) = name
-
隐式视图:用于类型间转换,或调用类中本不存在的方法(通过import)
def foo(msg : String) = println(msg)
implicit def intToString(x : Int) = x.toString
foo(10)
class SwingType{
def wantLearned(sw : String) = println("兔子已经学会了"+sw)
}
object swimming{
implicit def learningType(s : AminalType) = new SwingType
}
class AminalType
object AminalType extends App{
import com.mobin.scala.Scalaimplicit.swimming._
val rabbit = new AminalType
rabbit.wantLearned("breaststroke") //蛙泳
}
转换时机:
-
当方法中的参数的类型与目标类型不一致时
-
当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换
解析机制:
即编译器是如何查找到缺失信息的,解析具有以下两种规则:
1.首先会在当前代码作用域下查找隐式实体(隐式方法 隐式类 隐式对象)
2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找
类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下:
(1)如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析过程中,它们的伴生对象都会被搜索
(2)如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分,比如List[String]的隐式搜索会搜索List的
伴生对象和String的伴生对象
(3) 如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索
(4) 如果T是个类型注入S#T,那么S和T都会被搜索
转换前提:
1.不存在二义性
2.隐式操作不能嵌套使用,即一次编译只隐式转换一次(One-at-a-time Rule)
3.代码能够在不使用隐式转换的前提下能编译通过,就不会进行隐式转换。
22、函数柯里化(currying)
柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
隐式转换
中经常用到
//def add(x:Int,y:Int)=x+y
def add(x:Int)(y:Int)=x+y
add(1)(2)
21、获取类型
getClass/classof[]
p.getClass==classof[Person]
22、Option
Option存在的意义, 就是为了在代码中注明, 让大家一看就知道: "这个东西可能是空的! 用的时候给我小心点
val a:Option[A]=x
val b:A=if(a.isEmpty) 0 else Option[A]