Scala编程进阶

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala编程进阶相关的知识,希望对你有一定的参考价值。

跳出循环语句的3种方法... 1

多维数组... 2

Java数组与Scala数组缓冲的隐式转换... 2

Java MapScala Map的隐式转换... 3

Tuple拉链操作... 3

内部类的作用域:外部类对象... 3

扩大内部类作用域:伴生对象... 4

扩大内部类作用域:类型投影... 4

内部类获取外部类的引用... 4

package定义... 5

package特性... 5

import. 8

   

跳出循环语句的3种方法

方法一:使用boolean控制变量

while循环:

var flag = true

var res = 0

var n = 0

while(flag) {

  res += n

  n += 1

  if (n == 5) {

    flag = false

  }

}

 

for循环:(高级for循环,加上了if守卫)

var flag = true

var res = 0

for (i <- 0 until 10 if flag) {

  res += i

  if (i == 4) flag = false

}

方法二:在嵌套函数中使用return

def add_outer() = {

  var res = 0

  def add_inner() {

    for (i <- 0 until 10) {

      if (i == 5) {

        return

      }

      res += i

    }

  }

  add_inner()

  res

}

方法三:使用Breaks对象的break方法

java里面的break比较类似,相对来说,比较灵活好用;与breakable代码块配合使用

import scala.util.control.Breaks._

var res = 0

breakable {

  for (i <- 0 until 10) {

    if (i == 5) {

      break;

    }

    res += i

  }

}

多维数组

什么是多维数组?:数组的元素,还是数组,数组套数组,就是多维数组

构造指定行与列的二维数组:Array.ofDim方法

val multiDimArr1 = Array.ofDim[Double](3, 4)

multiDimArr1(0)(0) = 1.0

构造不规则多维数组:

val multiDimArr2 = new Array[Array[Int]](3)

multiDimArr2(0) = new Array[Int] (1)

multiDimArr2(1) = new Array[Int] (2)

multiDimArr2(2) = new Array[Int] (3)

multiDimArr2(1)(1) = 1

Java数组与Scala数组缓冲的隐式转换

Scala代码中,直接调用JDKJava)的API,比如调用一个Java类的方法,势必可能会传入Java类型的list,此时如果直接把ScalaArrayBuffer传入Java接收ArrayList的方法,肯定不行。这可以先将Scala中的Buffer转换为Java中的List即可

import scala.collection.JavaConversions.bufferAsJavaList

import scala.collection.mutable.ArrayBuffer

val command = ArrayBuffer("javac", "C:\\Users\\Administrator\\Desktop\\HelloWorld.java") // 调用操作系统命令编译源码

// ProcessBuilderJDK中的类,构造函数为:ProcessBuilder(List<String> command),要求的是List,所以需要将ScalaBuffer转换为Java中的List,才能在Java中使用

val processBuilder = new ProcessBuilder(command)

val process = processBuilder.start()

val res = process.waitFor()

 

下面是将Java返回的List转换为Buffer

import scala.collection.JavaConversions.asScalaBuffer

import scala.collection.mutable.Buffer

// ProcessBuildercommand()方法返回的是List<String>,所以需要将List<String>隐式转换为Buffer[String],才能在Scala中使用

val cmd: Buffer[String] = processBuilder.command()

Java MapScala Map的隐式转换

import scala.collection.JavaConversions.mapAsScalaMap

val javaScores = new java.util.HashMap[String, Int]()

javaScores.put("Alice", 10)

javaScores.put("Bob", 3)

javaScores.put("Cindy", 8)

val scalaScores: scala.collection.mutable.Map[String, Int] = javaScores // Java Map自动隐式转换 Scala Map

 

import scala.collection.JavaConversions.mapAsJavaMap

import java.awt.font.TextAttribute._

val scalaAttrMap = Map(FAMILY -> "Serif", SIZE -> 12)

val font = new java.awt.Font(scalaAttrMap) // Scala Map自动隐式转换 Java Map

Tuple拉链操作

Tuple拉链操作指的就是zip操作

zip操作,是Array类的方法,用于将两个Array,合并为一个Array

比如Array(v1)Array(v2),使用zip操作合并后的格式为Array((v1,v2))

合并后的Array的元素类型为Tuple

 

val students = Array("Leo", "Jack", "Jen")

val scores = Array(80, 100, 90)

val studentScores = students.zip(scores)

for ((student, score) <- studentScores)

  println(student + " " + score)

 

如果Array的元素类型是个Tuple,调用ArraytoMap方法,可以将Array转换为Map

studentScores.toMap

内部类的作用域:外部类对象

import scala.collection.mutable.ArrayBuffer

class Class {

  class Student(val name: String)

  val students = new ArrayBuffer[Student]

  def register(name: String) =  {

    new Student(name)

  }

}

val c1 = new Class

val leo = c1.register("leo")

c1.students += leo

 

val c2 = new Class

val jack = c2.register("jack")

c1.students += jack // error: type mismatch;

扩大内部类作用域:伴生对象

object Class {

  class Student(val name: String)

}

class Class {

  val students = new ArrayBuffer[Class.Student]

  def register(name: String) = {

    new Class.Student(name)

  }

}

val c1 = new Class

val leo = c1.register("leo")

c1.students += leo

 

val c2 = new Class

val jack = c2.register("jack")

c1.students += jack

扩大内部类作用域:类型投影

class Class {

  class Student(val name: String)

  val students = new ArrayBuffer[Class#Student] // 明确说明使用的是Class类型中的Student类,而非Class对象中的

  def register(name: String) =  {

    new Student(name)

  }

}

val c1 = new Class

val leo = c1.register("leo")

c1.students += leo

 

val c2 = new Class

val jack = c2.register("jack")

c1.students += jack

内部类获取外部类的引用

class Class(val name: String) {

  outer => //名随便

class Student(val name: String) {

    def introduceMyself = "Hello, I‘m " + name + ", I‘m very happy to join class " + outer.name

  }

  def register(name: String) =  {

    new Student(name)

  }

}

val c1 = new Class("c1")

val leo = c1.register("leo")

leo.introduceMyself

package定义

因为要对多个同名的类进行命名空间的管理,避免同名类发生冲突

比如说,scala.collection.mutable.Mapscala.collection.immutable.Map

 

 

package定义的第一种方式: 多层级package定义(比较差的做法,一般不这么干)

package com {

         package sn {

                   package scala {

                            class Test {}

                   }

         }

}

 

package定义的第二种方式: 串联式package定义(也不怎么样,一般也不这么干)

package com.sn.scala {

         package service {

                   class Test {}

         }

}

 

package定义的第三种方式: 文件顶部package定义

package com.sn.scala.service

class Test {

}

package特性

同一个包定义,可以在不同的scala源文件中的:

Test1.scala

package com {

  package sn {

    package scala {

      class Test1

    }

  }

}

 

Test2.scala

package com {

  package sn {

    package scala {

      class Test2

    }

  }

}

一个scala源文件内,可以包含两个包:

Test3.scala

package com {

  package sn {

    package scala1 {

      class Test

    }

  }

}

 

package com {

  package sn {

    package scala2 {

      class Test

    }

  }

}

子包中的类,可以访问父包中的类:

Test.scala

package com {

  package sn {

    package scala {

      object Utils {

        def isNotEmpty(str: String): Boolean = str != null && str != ""

      }

 

      class Test

 

      package service {

        class MyService {

          def sayHello(name: String) {

            if (Utils.isNotEmpty(name)) {

              println("Hello, " + name)

            } else {

              println("Who are you?")

            }

          }

        }

      }

    }

  }

}

 

object T {

  def main(args: Array[String]) {

    import com.sn.scala.service._

    new MyService().sayHello("")

    new MyService().sayHello("leo")

  }

}

 

相对包名与绝对包名:

package com {

  package sn {

    package scala {

      object Utils {

        def isNotEmpty(str: String): Boolean = str != null && str != ""

      }

 

      class Test

 

      package collection {}

 

      package service {

        class MyService {

          // 报错,默认使用相对报名,从com.sn.scala.collection包中,寻找mutable包下的ArrayBuffer

          // 但是找不到,所以会报错

          // val names = new scala.collection.mutable.ArrayBuffer[String]

 

          // 正确的做法是使用_root_,引用绝对包名

          val names = new _root_.scala.collection.mutable.ArrayBuffer[String]

 

          def sayHello(name: String) {

            if (Utils.isNotEmpty(name)) {

              println("Hello, " + name)

            } else {

              println("Who are you?")

            }

          }

        }

      }

    }

  }

}

 

定义package对象(比较少用):

package内的成员,可以直接访问package对象内的成员

package com.sn.scala

package object service {

  val defaultName = "Somebody"

}

 

package service {

  class MyService {

    def sayHello(name: String) {

      if (name != null && name != "") {

        println("Hello, " + name)

      } else {

        println("Hello, " + defaultName)//访问包对象中的成员

      }

    }

  }

}

 

package可见性:

package com.sn {

  package scala {

    class Person {

      //com.sn.scala包下可见

      private[scala] val name = "leo"

      //com.sn包下可见

      private[sn] val age = 25

    }

    object T1 {

      new Person().name

    }

  }

  object T2 {

    import com.sn.scala.Person

    new Person().age

  }

}

import

package com.sn.scala

package service {

  class MyService {

    def sayHello(name: String) {}

  }

}

 

import com.sn.scala.service.MyService;

object MainClass {

  def main(args: Array[String]): Unit = {

    val service = new MyService

  }

}

 

 

import特性一: import com.sn.scala.service._这种格式,可以导入包下所有的成员

 

import特性二: scalajava不同之处在于,任何地方都可以使用import,比如类内、方法内,这种方式的好处在于,可以在一定作用域范围内使用导入

object MainClass {

  def main(args: Array[String]): Unit = {

    import com.sn.scala.service._

    val service = new MyService

  }

}

 

import特性三: 选择器、重命名、隐藏

 

import com.sn.scala.service.{ MyService },仅仅导入com.sn.scala.service包下的MyService类,其它不导入

import com.sn.scala.service.{ MyService => MyServiceImpl },将导入的类进行重命名

import com.sn.scala.service.{ MyService => _, _ },导入com.sn.scala.service包下所有的类,但是隐藏掉MyService

 

import特性四: 隐式导入

每个scala程序默认都会隐式导入以下几个包下所有的成员

import java.lang._

import scala._

import Predef._

重写field的提前定义

默认情况下,如果父类中的构造函数代码用到了被子类重写(或实现)的field,那么可能会出现未被正确初始化的问题:

当父类的构造函数执行时,如果使用到了被子类实现或重写过的fieldfield重写或实现相当于对应的getter方法被重写),会调用子类重写或实现过的field,由于子类构造器还没有执行,所以会返回子类中被重写或实现过的该field的初始值(比如Int0Stringnull)。详细可以参数《Scala编程基础》中的“trait field的初始化”相应章节

class Student {

  val classNumber: Int = 10

  val classScores: Array[Int] = new Array[Int](classNumber) // 会调用子类被重写过的classNumber字段

}

 

class PEStudent extends Student {

  override val classNumber: Int = 3

}

scala> new PEStudent().classScores

res42: Array[Int] = Array()

本来我们期望的是,PEStudent,可以从Student继承来一个长度为3classScores数组,结果PEStudent对象,只有一个长度为0classScores数组

 

此时只能使用Scala对象继承的一个高级特性: 提前定义,在父类构造函数执行之前,先执行子类的构造函数中的某些代码

class Student {

  val classNumber: Int = 10

  val classScores: Array[Int] = new Array[Int](classNumber) // 会调用子类被重写过的classNumber字段

}

class PEStudent extends {

  override val classNumber: Int = 3

} with Student

scala> new PEStudent().classScores

res43: Array[Int] = Array(0, 0, 0)

 

也可以这样,但Student需定义成trait

trait Student {

  val classNumber: Int = 10

  val classScores: Array[Int] = new Array[Int](classNumber)

}

class PEStudent extends Student {

}

var ps = new {override val classNumber: Int = 3} with PEStudent with Student

ps.classScores

Scala的继承层级

这里我们大概知道一下Scala的继承层级,我们写的所有的Scala traitclass,都是默认继承自一些Scala根类的,有一些基础的方法

 

Scala中,最顶端的两个traitNothingNullNull trait唯一的对象就是null

其次是继承了Nothing traitAny

接着Anyval traitAnyRef类,都继承自Any

 

Any类是个比较重要的类,其中定义了isInstanceOfasInstanceOf等方法,以及equalshashCode等对象的基本方法

Any类,有点像Java中的Object基类

AnyRef类,增加了一些多线程的方法,比如waitnotify/notifyAllsynchronized等,也是属于Java Object类的一部分

对象相等性

这里,我们要知道,在scala中,你如何判断两个引用变量,是否指向同一个对象实例

 

AnyRefeq方法用于检查两个变量是否指向同一个对象实例

AnyRefequals方法默认调用eq方法实现,也就是说,默认情况下,判断两个变量相等,要求必须指向同一个对象实例

 

通常情况下,自己可以重写equals方法,根据类的fields来判定是否相等

此外,定义equals方法时,也最好使用同样的fields,重写hashCode方法

 

如果只是想要简单地通过是否指向同一个对象实例,判定变量是否相当,那么直接使用==操作符即可,默认判断null,然后调用equals方法

 

class Product(val name: String, val price: Double) {

  final override def equals(other: Any) = {

    val that = other.asInstanceOf[Product]

    if (that == null) false

    else name == that.name && price == that.price

  }

  final override def hashCode = 13 * name.hashCode + 17 * price.hashCode

}

 

 

 

 

 

 

 

 

 

 

 

 

 

附件列表

     

    以上是关于Scala编程进阶的主要内容,如果未能解决你的问题,请参考以下文章

    Spark 常用编程技巧

    Spark 常用编程技巧

    2小时速学大数据编程语言 Scala 秘籍

    Spark函数式编程进阶

    Scala思维导图

    大数据技术之_19_Spark学习_02_Spark Core 应用解析+ RDD 概念 + RDD 编程 + 键值对 RDD + 数据读取与保存主要方式 + RDD 编程进阶 + Spark Cor