Scala零基础教学【41-60】
第41讲:List继承体系实现内幕和方法操作源码揭秘
def main(args: Array[String]) { /** * List继承体系实现内幕和方法操作源码揭秘 * * List本身是一个抽象类 定义如下: * abstract sealed class List[+A] extends AbstractSeq[A] * with LinearSeq[A] * with Product * with GenericTraversableTemplate[A, List] * with LinearSeqOptimized[A, List[A]] * * List下的两个重要的子类Nil和:: * Nil 表示一个空值 定义为一个Cass Object: case object Nil extends List[Nothing] * :: 定义 final case class ::[B](private var hd: B, private[scala] var tl: List[B]) extends List[B] * */ //这种方式其实是 调用List的伴生对象的apply方法 val list: List[Int] val list: List[Int] = List(1, 2, 3, 4, 5) //这是一种"协变"的概念 Int为Any的子类 所以认为List[Int]的具体父类型可以是List[Any] val listAny: List[Any] = list println(list.isEmpty) println(list.head) println(list.tail) println(list.length) println(list.drop(2)) list.map(_ * 2) }
第42讲:Scala中泛型类、泛型函数、泛型在Spark中的广泛应用
/** * 定义一个泛型类[]中定义的就是未知的类型 只有赋值使用才能确定具体的类型 */ class Triple[F, S, T](val first: F, val second: S, val third: T) /** * Scala中泛型类、泛型函数、泛型在Spark中的广泛应用 */ object Hello_Type_Parameterization { def main(args: Array[String]) { //在定义后scala的类型推断会得出triple类型为 Triple[String, Int, Double] val triple = new Triple("Spark", 3, 3.1415) //显示声明类型 val bigData = new Triple[String, String, Char]("Spark", "Hadoop", 'R') //定义泛型类型 def getData[T](list : List[T]) = list(list.length / 2) //List(1)=>"Hadoop" println(getData(List("Spark", "Hadoop", 'R'))) val f = getData[Int] _ println(f(List(1,2,3,4,5,6)))//5 val queue = Queue(1,2,3,4,5) val queue_appended = queue enqueue 6 println("queue : " + queue + " " + "queue_appended : " + queue_appended) } }
第43讲:Scala中类型变量Bounds代码实战及其在Spark中的应用源码解析
/** * 类型的界定 * 这里的[T <: Comparable[T]] 表示类型T必须是Comparable[T]的子类 */ class Pair[T <: Comparable[T]](val first : T,val second : T){ def bigger = if(first.compareTo(second) > 0)first else second } /** * [R >: T]表示 R类型是T类型的父类 * 类型变量的界定 就R而言 T为R的下界 就T而言 R为T的上界 * */ class Pair_Lower_Bound[T](val first:T,val second:T){ def replaceFirst[R >: T](newFirst:R)= new Pair_Lower_Bound[R](newFirst,second) } object Typy_Variables_Bounds { def main(args: Array[String]){ /** * 49-57:0-9 * A-Z:66-90 * a-z:97-122 */ val schar='S' println(schar.toInt) println('H'.toInt) println("Spark".compareTo("Hadoop"))//11 val pair = new Pair("Spark", "Hadoop") println(pair.bigger)//Spark } }
第44讲:Scala中View Bounds代码实战及其在Spark中的应用源码解析
class Pair_NotPerfect2[T <: Comparable[T]](val first : T,val second : T){ def bigger = if(first.compareTo(second) > 0)first else second } /** * 视图界定 <% */ /** * Ordered视图界定 * 上面这种方式的12行first.compareTo(second) > 0 通过compareTo来比较 但是不能直观的像数学比较那样清晰 * Scala提供了Ordered视图界定 * Ordered在Comparable上提供一些关系型的操作符 < > <= >=等 */ class Pair_NotPerfect[T <% Comparable[T]](val first : T,val second : T){ def bigger = if(first.compareTo(second) > 0)first else second } class Pair_Better[T <% Ordered[T]](val first : T,val second : T){ def bigger = if(first > second)first else second } object View_Bounds { def main(args: Array[String]) { // val pair2 = new Pair_NotPerfect2(1, 3) // println(pair2.bigger) val pair = new Pair_NotPerfect("Spark", "Hadoop") println(pair.bigger) /* * 当类型界定为Pair_NotPerfect[T <: Comparable[T]]报错 因为Int本身不是Comparable的子类 * * 当类型界定为视图界定时 Pair_NotPerfect[T <% Comparable[T]] 就可以正常运行 * 是因为Int本身不是Comparable的子类型 Scala通过"隐式转换"将Int转换成RichInt 而这个类型是Comparable的子类 */ val pairInt = new Pair_NotPerfect(3, 5) //Int -> RichInt println(pairInt.bigger) /** * 注意:这样定义不是因为String的上界是Ordered[String],String不是Ordered[String]的子类 * 当使用视图界定时 会发生"隐式转换" 把String --> RichString * 而RichString是Ordered[RichString]的子类型 RichString中是实现了这样的 < > <= >=等方法 * 从而真正是让String类型完成视图界定 */ val pair_Better_String = new Pair_Better("Java", "Scala") //String -> RichString println(pair_Better_String.bigger) val pair_Better_Int = new Pair_Better(20, 12) println(pair_Better_Int.bigger) } }
第45讲:Scala中Context Bounds代码实战及其在Spark中的应用源码解析
/** * 上下文界定 [T : Ordering] 说明存在一个隐式的值Ordering[T] //implicit ordered: Ordering[T] * * Ordering源码声明: * trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable */ class Pair_Ordering[T : Ordering] (val first : T, val second : T){ //这是一个隐式转换的显式定义 这个函数没有参数 当时函数执行的时候 这个隐式值就会自动传进来 def bigger(implicit ordered: Ordering[T]) = { if (ordered.compare(first, second) > 0) first else second } } object Context_Bounds { def main(args: Array[String]) { val pair = new Pair_Ordering("Spark", "Hadoop") println(pair.bigger) val pairInt = new Pair_Ordering(3, 5) println(pairInt.bigger) } }
第46讲: ClassTag 、Manifest、ClassManifest、TypeTag代码实战及其在Spark中的应用源码解析
package com.wanji.scala.type_parameterization import scala.reflect.ClassTag class A[T] /** * ClassTag 、Manifest、ClassManifest、TypeTag代码实战及其在Spark中的应用源码解析 * * ClassTag ==> ClassManifest * TypeTag ==> Manifest */ object Manifest_ClassTag { def main(args: Array[String]) { /** * Q: 可以创建泛型数组吗? 理论上是不可以的,因为没有指定具体的,在Scala程序运行中,数组必须有具体的类型,没有否无法创建的相应的数组 * * 引出Manifest的概念可以创建泛型数组 * [T : Manifest]这样的写法被称之为Manifest上下文界定 实质上这是需要一个Manifest[T]类型的隐式对象 这又是一个"隐式转换"的过程 * 通过这个隐式的值来辅助构建泛型数组,来确定T的具体类型 * 所以在创建泛型函数时 需要Manifest的类型来辅助构建泛型数组,借助Manifest类型对象来指定泛型数组具体的类型 * * 通过Manifest[T]可以记录T的类型 在实际运行的时候我们获取T具体的类型 **/ def arrayMake[T: Manifest](first: T, second: T) = { val r = new Array[T](2) r(0) = first r(1) = second r } arrayMake(1, 2).foreach(println) /** * [T : ClassTag]这种写法说明 当这个函数在运行时 对存在一个ClassTag[T]一个隐式值 这种方式是最常用的 */ def mkArray[T: ClassTag](elems: T*) = Array[T](elems: _*) mkArray(42, 13).foreach(println) mkArray("Japan", "Brazil", "Germany").foreach(println) /** * Manifest的原生写法,不推荐 */ def manif[T](x: List[T])(implicit m: Manifest[T]) = { if (m <:< manifest[String]) //<:< 表示m是manifest[String]类型 println("List strings") else println("Some other type") } manif(List("Spark", "Hadoop")) manif(List(1, 2)) manif(List("Scala", 3)) val m = manifest[A[String]] println(m) val cm = classManifest[A[String]] println(cm) } }
第47讲:Scala多重界定代码实战及其在Spark中的应用源码解析
class M_A[T] class M_B[T] /** * scala多重界定代码实战及其在Spark中的应用源码解析 * * T<:A with B * T是A或者B的子类 * * T>:A with B * A或者B是T的子类 * * T>:A <:B (写法上 下界必须在前边 上届必须在后面) * T同时拥有下界A和上界B(A必须为B的子类型) 但是T不能同时拥有多个上界或者多个下界 * * T:A:B(上下文界定) * * T <% A <% B(视图界定) T必须能够同时转化为A和B的要求 * T可以< 同时>拥有多个视图界定 * T可以通过"隐式转换"为A 也可以"隐式转换"为B * */ object Multiple_Bounds { def main(args: Array[String]) { implicit val a = new M_A[Int] implicit val b = new M_B[Int] def foo[ T : M_A : M_B ](i:T) = println("OK") foo(2) } }
第48讲:Scala类型约束代码实战及其在Spark中的应用源码解析
/** * Scala类型约束代码实战及其在Spark中的应用源码解析 * * A =:= B 表示A类型等同于B类型 * A <:< B 表示A类型是B类型的子类型 */ object Type_Contraints { def main(args: Array[String]){ def rocky[T](i:T)(implicit ev: T <:< java.io.Serializable) { print("Life is short,you need spark!") } rocky("Spark") } }
第49讲:Scala中Variance代码实战及其在Spark中的应用源码解析
/** * Scala中Variance代码实战及其在Spark中的应用源码解析 * * 通俗讲 * B是A的子类 ==>List[B]是List[A]的子类(与具体元素的继承关系同向) 这样称之为"协变" * B是A的子类 ==>List[A]是List[B]的子类(与具体元素的继承关系反向) 这样称之为"逆变" * 如果支持上面的这种概念 就被称之为"Variance" 否则称之为"inVariance" * * 事实上Java不支持在定义一个类型时声明为这样的"Variance" * e.g. String是object的子类,List<String> 却不是 List<Object>的子类 * 但是Java中是存在这样的痕迹的 比如: * List<? extends Object> list = new ArrayList<String>() * 在Scala中也是可以向上面这样写的: * val list: List[_ <: Any] = List[String]("Spark", "Hadoop") * * 事实上Java支持在使用的时候 可以这样去定义,在声明的时候不支持,但是Scala中的可以 * * Scala中 在声明时留意表达这种Variance的关系 * 形如class C[+T] “+”表示"协变" 也就是说若B为A的子类型 则C[B]是C[A]的子类型 * class C[-T] “-”表示"协变" 也就是说若B为A的子类型 则C[A]是C[B]的子类型 * */ class Person class Student extends Person class C[+T](val args: T) class S[+T](arg : T) extends C[T](arg) trait Friend[-T]{ def makeFriend(somebody: T) } object Variance { def makeFriendWithYou(s: Student, f: Friend[Student]){f.makeFriend(s)} def main(args: Array[String]) { val value : C[Person] = new C[Student](new Student) // List<? extends Oject> list = new ArrayList<String>() val list : List[_ <: Any] = List[String]("Spark") } }
第50讲:Scala中Variance变化点及其在Spark中的应用源码解析
package com.wanji.scala.type_parameterization //class P[+T](val first: T, val second: T) class P[+T](val first: T, val second: T){ // def replaceFirst(newFirst: T) = new P[T](newFirst, second) //方法是泛型的 方法的参数是逆变点 返回值是协变的协变点 def replaceFirst[R >: T](newFirst: R) = new P[R](newFirst, second) } object Variant_Positions { def main(args: Array[String]) { } }
第51讲:Scala中链式调用风格的实现代码实战及其在Spark编程中的广泛运用
package com.wanji.scala.type_parameterization class Animal { def breathe:this.type=this } class Cat extends Animal { def eat :this.type= this } object Singleton_Types { def main(args: Array[String]): Unit = { /* * 代码2 报错 * * cat.breathe 返回的是Animal的this Animal实例没有eat方法 所以报错 * */ /* * * 为了到达链式调用 采用代码1 * * 注意:this.type = this * * * Q:type是指什么? * * A:在Scala中 任何类对象都有一个type属性 * * 当执行cat.breathe其实返回的Cat类实例的type 而这个type有eat方法 * */ val cat = new Cat cat.breathe.eat } }
第52讲:Scala中路径依赖代码实战详解
val outer = new Outer val inner = new outer.Inner /** * Scala中的内部类 必须依赖于外部类的实例 而外部类的实例各不相同 * 所以被之为这种对于外部类的依赖为"路径依赖" * 所以不同的路径代表不同的类型 */ val inner2: outer.Inner = new outer.Inner println(inner) println(inner2) val o1 = new Outer val o2 = new Outer //报错 o1与o2不是同样的实例 //val i2: o2.Inner = new o1.Inner val i: Outer#Inner = new o1.Inner //o1.Inner是Outer#Inner的子类。外部类#内部类:类型投影(不同外部类实例,但内部类是同一类型)。 //虽有路径依赖,但还想用Java风格就用这种表达方式。
例如:
有2个社交网络,facebook和twitter,有很多会员,是依赖于各自网络的。
就算是同一个人在这两个不同的社交网络里,也是不同的实例。
spark编程中,数据是分布式的,会分成很多的片,不同分片的数据从理论上讲是一样的(当然数据内容不同),也就是说,属于同样类型的数据,但是我们写代码时,处理数据时,还是处理属于每一个blog或split分片的结果。从这个角度看,也可看作是路径依赖。
第53讲:Scala中结构类型实战详解
/** * Scala中结构类型实战详解 * 结构类型不关心传入的类型 只关心传入的对象具有某种行为 */ class Structural { def open()=print("A class instance Opened") } object Structural__Type { def main(args: Array[String]){ init(new { def open()=println("Opened") }) /** * type的作用是把“=”右边的内容起个别名 */ type X = { def open():Unit } def init(res:X) = res.open init(new { def open()=println("Opened again") }) /** * 定义单例对象 */ object A { def open() {println("A single object Opened")} } init(A) val structural = new Structural init(structural) } /** * 从函数的定义来看,并不关心传入的对象为何,只关心传入的对象必须具有open方法 * */ def init( res: {def open():Unit} ) { res.open } }
第54讲:Scala中复合类型实战详解
trait Compound_Type1; trait Compound_Type2; class Compound_Type extends Compound_Type1 with Compound_Type2 object Compound_Type { def compound_Type(x: Compound_Type1 with Compound_Type2) = { println("Compound Type in global method") } def main(args: Array[String]) { compound_Type(new Compound_Type1 with Compound_Type2) object compound_Type_oject extends Compound_Type1 with Compound_Type2 compound_Type(compound_Type_oject) type compound_Type_Alias = Compound_Type1 with Compound_Type2 def compound_Type_Local(x:compound_Type_Alias) = println("Compound Type in local method") val compound_Type_Class = new Compound_Type compound_Type_Local(compound_Type_Class) type Scala = Compound_Type1 with Compound_Type2 { def init():Unit } } }
第55讲:Scala中Infix Type实战详解
def main(args: Array[String]) { object Log { def >>:(data:String):Log.type = { println(data); Log } } "Hadoop" >>: "Spark" >>: Log val list = List() val newList = "A" :: "B" :: list println(newList) class Infix_Type[A,B] val infix: Int Infix_Type String = null val infix1: Infix_Type[Int, String] = null case class Cons(first:String,second:String) val case_class = Cons("one", "two") case_class match { case "one" Cons "two" => println("Spark!!!") } //unapply }
第56讲:Scala中Self Types实战详解
/** * Scala中Self Types实战详解 */ class Self { self => //用法一:self => 表示this的别名 这是self和this等价 注意不能使用this作为别名 val tmp="Scala" def foo = self.tmp + this.tmp } trait S1 class S2 { /** * 用法二: * 这种方式和self =>并不一样 将S1比如为摸个trait是 * 这种this:S1为this的别名时 有一个强制的要求 * (1)在该类型实例化时 必须混入这个类型 即: val c = new S2 with S1 否则报错 * (2)在继承该类的子类是 也必须混入 比如:class S3 extends S2 with S1 若不混入with S1则报错 * */ this:S1 => } class S3 extends S2 with S1 trait T { this:S1 => } object S4 extends T with S1 object Self_Types { def main(args: Array[String]) { class Outer { outer => val v1 = "Spark" class Inner { /** * 用法三: * 这里内部类需要访问外部类成员和方法 * 通过定义了outer =>代替了外部类Outer的this * 在内部类理由直接引用不需要考虑this是谁this * 这是使用这种方式声明的好处 * 如果写成println(this.v1)则报错 因为这个this代表了Inner的this * 下面的三种写法都是正确的 */ println(outer.v1) println(v1) println(Outer.this.v1) } } val c = new S2 with S1 } }
第57讲:Scala中Dependency Injection实战详解
trait Logger { def log (msg : String) } trait Auth { auth : Logger => def act(msg : String) { log(msg) } } object DI extends Auth with Logger { override def log(msg : String) = println(msg); } object Dependency_Injection { def main(args: Array[String]) { DI.act("I hope you'll like it") } }
第58讲:Scala中Abstract Types实战详解
import scala.io.Source import scala.io.BufferedSource trait Reader{ /** * 用type关键字 声明一个In类型(称为"抽象类型") * 但是没有指明具体类型是什么类型 需要在它的实现类中指明具体的类型 * 在声明抽象类型时 可以对类型进行限定 */ type In <: java.io.Serializable type Contents /** *在抽象对的可以使用抽象类型 */ def read(in: In): Contents } class FileReader extends Reader { type In = String //实现中具体的类型 type Contents = BufferedSource //type Contents=Contents override def read(name: In) = Source.fromFile(name) } object Abstract_Types { def main(args: Array[String]) { val fileReader = new FileReader val content = fileReader.read("F:\\1.txt") for (line <- content.getLines){ println(line) } } }
第59讲:Scala中隐式转换初体验实战详解以及隐式转换在Spark中的应用源码解析
import scala.io.Source import java.io.File /** * Scala中隐式转换初体验实战详解以及隐式转换在Spark中的应用源码解析 */ //这里的RichFile相当于File的增强类 需要的将要增强的类作为参数传入构造器中 class RichFile(val file:File){ def read = Source.fromFile(file.getPath()).mkString } object Context{ //File --> RichFile //implicit是隐式转换的关键字 这里定义一个隐式转换函数把当前类型转换成增强的类型 implicit def file2RichFile(file:File)= new RichFile(file) //File -> RichFile } object Hello_Implicit_Conversions { def main(args: Array[String]) { import Context.file2RichFile //File类本身没有read方法 通过隐式转换完成 //这里的read方法是RichFile类中的方法 需要通过隐式转换File --> RichFile println(new File("F:\\1.txt").read) } }
第60讲:Scala中隐式参数实战详解以及隐式参数在Spark中的应用源码解析
/** * Scala中隐式参数实战详解以及隐式参数在Spark中的应用源码解析 */ object Context_Implicits{ implicit val default:String = "Flink" } object Param{ def print(content:String)(implicit language:String){ println(language+":"+content) } } object Implicit_Parameters { def main(args: Array[String]) { Param.print("Spark")("Scala") import Context_Implicits._ //隐式参数没有传值,编译器会在全局范围内搜索 有没有implicit String类型的隐式值 并传入 Param.print("Hadoop") } }