Spark2.x学习笔记:2、Scala简单例子

2、 Scala简单例子

参考教程:https://yq.aliyun.com/topic/69

2.1 交互式编程

spark-shell是Spark交互式运行模式,提供了交互式编程,边敲代码边执行,不需要创建程序源文件,方便调试程序,有利于快速学习Spark。

[root@node1 spark-2.2.0]# bin/spark-shell 
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
17/09/03 06:32:38 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
17/09/03 06:32:56 WARN ObjectStore: Failed to get database global_temp, returning NoSuchObjectException
Spark context Web UI available at http://192.168.80.131:4040
Spark context available as 'sc' (master = local[*], app id = local-1504434761542).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.2.0
      /_/

Using Scala version 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_112)
Type in expressions to have them evaluated.
Type :help for more information.

scala> 

Spark内置了Scala环境,进入spark-shell后可以看到scala>,可以直接输入scala语句,回车即执行。

2.3 数据类型

Scala 极度重用了 Java 类型,Scala 的 Int 类型代表了 Java 的原始整数类型 int,Float 代表了 float,Boolean 代表了 boolean,数组被映射到 Java 数组。Scala 同样重用了许多标准 Java 库类型。例如,Scala 里的字符串文本是 Java.lang.String,而抛出的异常必须是 java.lang.Throwable 的子类。

scala> var x:Int =10
x: Int = 10

scala> var y:Double =3.14
y: Double = 3.14

scala> var s:String ="Hello,Scala!"
s: String = Hello,Scala!

scala> var b:Boolean=true
b: Boolean = true

scala>

备注:Scala语句的分号是可选的,且通常不写

2.2 Scala 变量

在 Scala 中,使用关键词 “var” 声明变量,使用关键词 “val” 声明常量。在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。 所以,如果在没有指明数据类型的情况下声明变量或常量必须要给出其初始值,否则将会报错。

scala> var x=1000
x: Int = 1000

scala> var y=3.14
y: Double = 3.14

scala> val s="hello,world"
s: String = hello,world

scala>

备注:val 变量的值只能初始化一次,再次赋值会发生错误,var 和 Java 的变量相同,可以随时修改。val 是函数式编程的风格,变量一旦赋值就不要再做修改。

2.3 基本运算

(1)算术运算

scala> 1+2
res1: Int = 3

scala> 2*3-5/2
res2: Int = 4

scala> 7%3
res3: Int = 1

scala> 

(2)关系运算

scala> var a=5
a: Int = 5

scala> var b=3
b: Int = 3

scala> a==b
res7: Boolean = false

scala> a>=b
res8: Boolean = true

scala> a!=b
res9: Boolean = true

scala> 

(3)逻辑运算

scala> var a=true;
a: Boolean = true

scala> var b=false;
b: Boolean = false

scala> a && b
res4: Boolean = false

scala> a || b
res5: Boolean = true

scala> !(a&&b)
res6: Boolean = true

scala>

(4)赋值运算符

scala> var x=2
x: Int = 2

scala> x+=3

scala> println(x)
5

scala> x*=3

scala> println(x)
15

scala> 

备注:

  • Scala没有提供++和–的预算符
  • println 是 Scala 预定义导入的标准输出函数,所以可以直接使用

(5)运算符重载 Scala允许方法调用形式a.fun(b)简写为a fun b

scala> 1.to(10)
res15: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> 1 to 10
res16: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> "hello" + ",world"
res17: String = hello,world

scala> "hello".+(",world")
res18: String = hello,world

scala>

也就是说

  • 字符串的+运算实际上就是调用了+方法,运算符被重载了
  • to运算可以生成一个区间集合

2.4 分支语句

scala> var x=3
x: Int = 3

scala> if(x<10)
     |   println("x<10")
x<10

scala> if(x<10){
     |   println("x<10")
     | }else{
     |   println("x>10")
     | }
x<10

scala>
scala> var x=10
x: Int = 10

scala> if(x<0){
     |   println("x<0")
     | }else if(x<10){
     |   println("0<=x<10")
     | }else{
     |   println("x>=10")
     | }
x>=10

scala>

2.4 循环语句

(1)while循环

scala> var i=1
i: Int = 1

scala> while(i<=100){
     |   sum+=i
     |   i=i+1
     | }

scala> println(sum)
5050

scala> 

(2)for循环 Scala的for循环与Java增强型for循环类似,基本形式是for a <- 集合,a相当于集合的通项元素,用于遍历集合,<-箭头符号类似于Java增强型for循环的冒号,<-表示生成器。

scala> var sum=0
sum: Int = 0

scala> for(i <- 1 to 100){
     |   sum+=i
     | }

scala> println(sum)
5050

scala>

备注:Scala在for循环中对循环变量i的赋值用了“<-”符号,1 to 100指定了一个范围

在scala中还有一个和上面的to关键字有类似作用的关键字until,它的不同之处在于不包括最后一个元素

scala> for(i <- 1 until 10) {
     |     println("i is " + i);
     | }
i is 1
i is 2
i is 3
i is 4
i is 5
i is 6
i is 7
i is 8
i is 9

scala>

2.5 函数

首先,函数/变量同是一等公民,函数与变量同等地位,函数的定义可以单独定义,可以不依赖于类、接口或者object,而且独立存在,独立使用,并且可以赋值给变量。 Spark当中的计算都是用scala函数式编程来做。 (1)库函数

import scala.math._
import scala.math._

scala> val x=2
x: Int = 2

scala> sqrt(x)
res4: Double = 1.4142135623730951

scala> 

备注:在Scala中,_字符是“通配符”,类似Java中的*

(2)自定义函数 函数的定义用 def 开始。每个函数参数后面必须带前缀冒号的类型标注,因为 Scala 编译器没办法推断函数参数类型。Scala 函数定义格式如下:

def functionName ([参数列表]) : [return type] = {
   function body
   return [expr]
}

定义一个求解最大值的函数

scala> def max(x: Int, y: Int): Int = if(x < y) y else x
max: (x: Int, y: Int)Int

scala> max(3,5)
res9: Int = 5

scala> 

备注:Scala函数可以没有return 语句,默认返回最后一个值。

如果函数的参数在函数体内只出现一次,则可以使用下划线代替

scala> def mul(x:Int,y:Int)=x*y
mul: (x: Int, y: Int)Int

scala> mul(2,3)
res26: Int = 6

scala> def mul=(_:Int)*(_:Int)
mul: (Int, Int) => Int

scala> mul(3,4)
res27: Int = 12

scala> 

(3)可变参数 Scala允许指定函数的最后一个参数可重复。

scala> def prints(args:String*)={
     |   for(arg <- args){
     |      println(arg)
     |   }
     | }
prints: (args: String*)Unit

scala> prints("aa","bb","cc")
aa
bb
cc

scala> 

备注:如果函数没有返回值,可以返回为 Unit,这个类似于 Java 的 void

(4)函数赋值 可以将一个函数赋值给一个变量, val 变量名 = 函数名+空格+_ 这里函数名后面必须要有空格,表明是函数的原型

scala> val fmax=max _
fmax: (Int, Int) => Int = <function2>

scala> fmax(3,5)
res7: Int = 5

scala>

(5)匿名函数 匿名函数格式: val 变量名 = (参数:类型) => 函数体

scala> var increase = (x: Int) => x + 1  
increase: Int => Int = <function1>

scala> var n=1;
n: Int = 1

scala> println(increase(n))
2

scala> 

程序说明:

  • (x: Int) => x + 1定义了一个匿名函数,=>表示对左边的参数进行右边的加工
  • 将该匿名函数赋值到 increase变量,通过函数变量即可像普通函数操作了

(6)高阶函数 因为函数的参数可以是变量,而函数又可以赋值给变量,即函数和变量地位一样,所以函数参数也可以是函数。Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。

普通函数的定义语法如下:

def funName(para1:Type1,para2:Type2):Type = { do some things }

高阶函数其实就是普通函数中的参数进一步推广了,高阶函数的参数可以是一个函数,参数名就是函数名,那么该特殊参数对应的类型怎么写呢?这样就把问题转化为寻找函数的类型的问题。函数的类型,其实就是输入输出的类型。 请看下面例子:

scala> import scala.math._
import scala.math._

scala> def valueFor(f:(Double)=>Double,value:Double)={
     |     f(value)
     | }
valueFor: (f: Double => Double, value: Double)Double

scala> valueFor(ceil _,0.25)
res1: Double = 1.0

scala> valueFor(sqrt _,0.25)
res2: Double = 0.5

scala> 

说明:

  • valueFor第一个参数是函数参数,f是参数名称,(Double)=>Double 是参数的类型;
  • 第二个参数是普通参数,参数名是value,参数类型是Double;
  • valueFor函数定义可以简写成def valueFor(f:(Double)=>Double,value:Double)=f(value)

再看一个例子: map方法接受一个函数参数,将它应用到数组中的每个元素,返回新的数组。

scala> import scala.math._
import scala.math._

scala> val num  = 3.14
num: Double = 3.14

scala> val func = ceil _
func: Double => Double = <function1>

scala> val array = Array(1.0,3.14,4).map(func) 
array: Array[Double] = Array(1.0, 4.0, 4.0)

scala> for(i<-array)print(i+" ")
1.0 4.0 4.0 
scala> 

(7)闭包 闭包可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。

scala> var factor = 3 
factor: Int = 3

scala> val multiplier = (i:Int) => i * factor  
multiplier: Int => Int = <function1>

scala> println(multiplier(1))
3

scala> println(multiplier(2))
6

scala> 

(8)柯里化(Currying) 柯里化函数是将原来接手两个参数的函数转变成新的接收一个参数的函数过程。新函数返回一个以原有第二个参数作为参数的函数。

柯里化函数定义

scala> def mul(x:Int)=(y:Int)=>x*y
mul: (x: Int)Int => Int

scala> mul(2)(3)
res19: Int = 6

scala> (mul(2))(3)
res23: Int = 6

scala> 

说明:mul(2)(3)实际上是按照(mul(2))(3)形式计算的,mul(2)的结果(y:Int)=>2*y,这个新函数又接收参数3得到结果6。

Scala可以简写柯里化函数

scala> def mul(x:Int)(y:Int)=x*y
mul: (x: Int)(y: Int)Int

scala> mul(5)(3)
res20: Int = 15

再看一个例子

scala> def strcat(s1: String)(s2: String) = s1 + s2
strcat: (s1: String)(s2: String)String

scala> strcat("hello")("world")
res22: String = helloworld

scala> 

2.6 字符串

scala> val msg = "hello"
msg: String = hello

scala> println(msg.length)
5

scala> println(msg.charAt(0))
h

scala> println(msg.compareTo("hi"))
-4

scala> println(msg.equals("hello"))
true

scala> val s=msg+",spark"
s: String = hello,spark

scala> println(s.substring(6))
spark

scala> 

2.7 数组

scala> var a1 = Array("QQ", "Baidu", "Google")
a1: Array[String] = Array(QQ, Baidu, Google)

scala> for(x<-a1)println(x)
QQ
Baidu
Google

scala> a1.foreach(x => println(x))
QQ
Baidu
Google

scala> a1.foreach(println(_))
QQ
Baidu
Google

scala> a1.foreach(println)
QQ
Baidu
Google
scala> var a2 =new Array[String](3)
a2: Array[String] = Array(null, null, null)

scala> array(0)="hello"
scala> a2(0)="hello"

scala> a2(1)="spark"

scala> a2(2)="!"

scala> println(a2(1))
spark

scala> 

Array是定长数组,而ArrayBuffer是可变数组。ArrayBuffer对应于Java中的ArrayList。

scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer

scala> val ab=ArrayBuffer[Int]()
ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()

scala> ab += 1
res7: ab.type = ArrayBuffer(1)

scala> ab += (2,3,4,5)
res8: ab.type = ArrayBuffer(1, 2, 3, 4, 5)

scala> val array=Array(7,11,13,17)
array: Array[Int] = Array(7, 11, 13, 17)

scala> ab ++= array
res9: ab.type = ArrayBuffer(1, 2, 3, 4, 5, 7, 11, 13, 17)

scala> ab.foreach(println)
1
2
3
4
5
7
11
13
17

scala> val a1=ab.toArray
a1: Array[Int] = Array(1, 2, 3, 4, 5, 7, 11, 13, 17)

scala> val buffer=a1.toBuffer
buffer: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 7, 11, 13, 17)

scala>

备注:

  • +=表示在ArrayBuffer尾部添加元素,尾部添加多个元素时用括号包起来
  • ++=表示追加任何集合
  • toArray表示将ArrayBuffer转换为Array
  • toArrayBuffer表示将Array转换为ArrayBuffer

2.8 List

Scala 列表类似于数组,它们所有元素的类型都相同,但是它们也有所不同:列表是不可变的,值一旦被定义了就不能改变,其次列表 具有递归的结构(也就是链接表结构)而数组不是。。 列表的元素类型 T 可以写成 List[T]。

Scala的List,scala.List,不同于Java的java.util.List,总是不可变的(而Java的List可变)。更通常的说法,Scala的List是设计给函数式风格的编程用的。

scala> val fruit: List[String] = List("apples", "oranges", "pears")
fruit: List[String] = List(apples, oranges, pears)

scala> println(fruit(0))
apples

scala> println(fruit.head)
apples

scala> println(fruit.tail)
List(oranges, pears)

scala> println(fruit.isEmpty)
false

scala> fruit.length
res24: Int = 3

scala> fruit.foreach(x => println(x))
apples
oranges
pears

scala> fruit.foreach(println)
apples
oranges
pears

scala>  

可以使用 ::: 运算符或 List.:::() 方法或 List.concat() 方法来连接两个或多个列表

Nil 也可以表示为一个空列表。

scala> val newFruit="bananas"::fruit
newFruit: List[String] = List(bananas, apples, oranges, pears)

scala> println(newFruit(0))
bananas

scala>
scala> val nums =List(-19,-7,0,3,11,19)
nums: List[Int] = List(-19, -7, 0, 3, 11, 19)

scala> nums.filter(_>0)
res30: List[Int] = List(3, 11, 19)

scala> nums.exists(_<0)
res31: Boolean = true

scala> nums.partition(_>0)
res34: (List[Int], List[Int]) = (List(3, 11, 19),List(-19, -7, 0))

scala>

说明: 集合中使用下划线是最常用的形式,下划线代表了集合中的“某(this)”一个元素。nums.filter(_>0)等价于nums.filter(x=>x>0),其中x=>x>0是匿名函数,且参数在函数体中只出现一次,可以简写成_>0

2.9 元祖

与列表一样,元组也是不可变的,但与列表不同,元组可以包含不同类型的元素。而列表应该是List[Int]或List[String]的样子,元组可以同时拥有Int和String。元组很有用,比方说,如果你需要在方法里返回多个对象。Java里你将经常创建一个JavaBean样子的类去装多个返回值,Scala里你可以简单地返回一个元组。而且这么做的确简单:实例化一个装有一些对象的新元组,只要把这些对象放在括号里,并用逗号分隔即可。一旦你已经实例化了一个元组,你可以用点号,下划线和一个基于1的元素索引访问它。

scala> val pa = (40,"Foo")
pa: (Int, String) = (40,Foo)

scala> val pair = (40,"Foo")
pair: (Int, String) = (40,Foo)

scala> println(pair)
(40,Foo)

scala> println(pair._1)
40

scala> println(pair._2)
Foo

scala> 

你或许想知道为什么你不能像访问List里的元素那样访问元组的,就像pair(0)。那是因为List的apply方法始终返回同样的类型,但是元组里的或许类型不同。这些_N数字是基于1的,而不是基于0的,因为对于拥有静态类型元组的其他语言,如Haskell和ML,从1开始是传统的设定。

2.10 Map

Scala映射(Map)是一组键/值对的对象。 任何值都可以根据键来进行检索。键在映射中是唯一的,但值不一定是唯一的。映射也称为哈希表。映射有两种,不可变的和可变的。可变对象和不可变对象之间的区别在于,当对象不可变时,对象本身无法更改。

默认情况下,Scala使用不可变映射(Map)。如果要使用可变集合(Set),则必须明确导入scala.collection.mutable.Map类。如果想同时使用可变的和不可变映射(Map),那么可以继续引用不可变映射(Map),但是可以将mutable集合引用mutable.Map。

scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map

scala> var map = Map(1 -> "a", 2 -> "b", 3 -> "c") 
map: scala.collection.mutable.Map[Int,String] = Map(2 -> b, 1 -> a, 3 -> c)

scala> println(map.keys)
Set(2, 1, 3)

scala> println(map.values)
HashMap(b, a, c)

scala> println(map.isEmpty)
false

scala> map += (4 -> "d")   
res12: scala.collection.mutable.Map[Int,String] = Map(2 -> b, 4 -> d, 1 -> a, 3 -> c)

scala> map.foreach(value => print(value + " "))   
(2,b) (4,d) (1,a) (3,c) 
scala>

总结: scala提供了许多用于添加和移除元素的操作符,总结如下。

  • 向后(:+),向前(+:)追加元素到有序集合
  • 添加(+)元素到无序集合
  • 用-移除元素
  • 用++和–来批量添加和移除元素
  • 对于列表,优先使用::和:::

2.10 将函数映射到集合

任何一种函数式语言中,都有map函数与faltMap这两个函数

  • map函数的用法,顾名思义,将一个函数传入map中,然后利用传入的这个函数,将集合中的每个元素处理,并将处理后的结果返回。
  • 而flatMap与map唯一不一样的地方就是传入的函数在处理完后返回值必须是List,其实这也不难理解,既然是flatMap,那除了map以外必然还有flat的操作,所以需要返回值是List才能执行flat这一步。
scala> val nums=List(1,2,3,4)
nums: List[Int] = List(1, 2, 3, 4)

scala> nums.map(x=>2+x)
res24: List[Int] = List(3, 4, 5, 6)

scala> nums.map(_+1)
res25: List[Int] = List(2, 3, 4, 5)

scala> 
scala> val data = List("Hadoop","Java","Spark")
data: List[String] = List(Hadoop, Java, Spark)

scala>  println(data.flatMap(_.toList))
List(H, a, d, o, o, p, J, a, v, a, S, p, a, r, k)

scala>

2.11 正则表达式

(1)模式匹配 模式匹配包括一系列备选项,每个替代项以关键字大小写为单位。每个替代方案包括一个模式和一个或多个表达式,如果模式匹配,将会进行评估计算。箭头符号=>将模式与表达式分离。

scala> def matchTest(x: Int): String = x match {
     |       case 1 => "one"
     |       case 2 => "two"
     |       case _ => "many"
     | }
matchTest: (x: Int)String

scala> matchTest(1)
res37: String = one

scala> matchTest(2)
res38: String = two

scala> matchTest(3)
res39: String = many

scala>

(2)正则表达式 scala.util.matching包中提供的Regex类支持和实现正则表达式。 以下实例演示了使用正则表达式查找单词 Scala

scala> import scala.util.matching.Regex
import scala.util.matching.Regex

scala> val pattern = "Scala".r
pattern: scala.util.matching.Regex = Scala

scala> val str = "Scala is Scalable and cool"
str: String = Scala is Scalable and cool

scala> println(pattern findFirstIn str)
Some(Scala)

scala> 

说明:

  • 使用 String 类的 r() 方法构造了一个Regex对象。
  • 使用 findFirstIn 方法找到首个匹配项。
  • 如果需要查看所有的匹配项可以使用 findAllIn 方法。

你可以使用 mkString( ) 方法来连接正则表达式匹配结果的字符串,并可以使用管道(|)来设置不同的模式

scala> val pattern = new Regex("(S|s)cala")
pattern: scala.util.matching.Regex = (S|s)cala

scala> val str = "Scala is scalable and cool"
str: String = Scala is scalable and cool

scala> println((pattern findAllIn str).mkString(","))
Scala,scala

scala>

程序说明: - (S|s)cala表示首字母可以是大写 S 或小写 s - mkString(“,”)使用逗号 , 连接返回结果

scala> val pattern = """(\d{1,3}\.){3}\d{1,3}""".r
pattern: scala.util.matching.Regex = (\d{1,3}\.){3}\d{1,3}

scala> val str="my ip is 192.168.1.81"
str: String = my ip is 192.168.1.81

scala> println(pattern findAllIn str toList)
warning: there was one feature warning; re-run with -feature for details
List(192.168.1.81)

scala> for(matchStr <- pattern.findAllIn(str)){
     |     println(matchStr)
     | }
192.168.1.81

scala> 

2.12 异常处理

scala> import java.io.FileReader
import java.io.FileReader

scala> import java.io.FileNotFoundException
import java.io.FileNotFoundException

scala> import java.io.IOException
import java.io.IOException

scala> try{
     |    val file=new FileReader("input.txt")
     | }catch{
     |    case ex: FileNotFoundException =>{
     |        println("Missing file exception")
     |    }
     |    case ex: IOException => {
     |        println("IO Exception")
     |    }
     | }
Missing file exception

scala> 

2.13 类与对象

(1)类的定义 public 是 Scala 的缺省访问级别

scala> class Point(x1:Int,y1:Int){
     |     var x:Int=x1
     |     var y:Int=y1
     |     
     |     def move(dx:Int,dy:Int){
     |           x=x+dx;
     |           y=y+dy;
     |     }
     |     def print(){
     |           println("("+x+","+y+")")
     |     }
     | }
defined class Point

scala> var p=new Point(10,20)
p: Point = Point@3e7f7cd2

scala> p.move(2,3)

scala> p.print()
(12,23)

scala> p.print
(12,23)

程序说明:

  • 构造器的参数直接放到类名之后。
  • 由于public是缺省的,所以Point类的x和y字段是public字段。
  • Scala对每个字段都提供了getter和setter方法,在生成面向JVM的类时,每个字段生成一个私有字段以及对应的getter和setter方法,这两个方法是public,在需要时可以重新定义getter和setter。(对于私有字段生成的getter和setter方法也是私有的。)
  • 调用无参方法,可以省略圆括号。建议对于修改对象值的方法使用圆括号,只是读值的方法不使用圆括号。

(2)辅助构造器

scala> class Point{
     |     var x:Int=0
     |     var y:Int=0
     |    
     |     def this(x:Int){
     |        this()
     |        this.x=x
     |     }
     | 
     |     def this(x:Int,y:Int){
     |        this(x)
     |        this.y=y
     |     }
     | }
defined class Point

scala> val p1=new Point
p1: Point = Point@6f694dd2

scala> var p2=new Point(1)
p2: Point = Point@7437ec41

scala> var p3=new Point(1,2)
p3: Point = Point@630643f6

scala> 

程序说明:

  • 一个类没有显示定义构造器,则自动拥有一个无参构造器
  • 辅助构造器的名称是this

(3)单例对象 Scala比Java更面向对象,因为在Scala中不能拥有静态成员,Scala它使用单例对象。单例是一种只能有一个实例的对象。使用object关键字对象而不是class关键字创建单例。由于无法实例化单例对象,因此无法将参数传递给主构造函数。 object下的成员都是静态的,若有同名的class,这其作为它的伴生类。在object中一般可以为伴生类做一些初始化等操作

(4)伴生对象 在Java或C++中,通常会用到既有实例方法也有静态方法的类,在Scala中将静态成员分离出来,形成与类同名的伴生对象(companion object)。类和它的伴生对象必须定义在同一个源文件中。类被称为是这个单例对象的伴生类(companion class)。

例子如下,直接将伴生类和伴生对象定义在同一源文件中即可。

class Account {
    val id = Account.newUniqueNumber()
    private var balance = 0.0
    def deposit(amount: Double){ balance += amount }
}

object Account { //伴生对象
    private var lastNumber = 0
    def newUniqueNumber() = { lastNumber += 1; lastNumber}
}

在spark-shell模式下,要同时定义类和对象,必须用粘贴模式。键入:paste,然后键入或粘贴类和对象的定义,最后一Ctrl+D退出粘贴模式。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Account {
    val id = Account.newUniqueNumber()
    private var balance = 0.0
    def deposit(amount: Double){ balance += amount }
}

object Account { //伴生对象
    private var lastNumber = 0
    def newUniqueNumber() = { lastNumber += 1; lastNumber}
}

// Exiting paste mode, now interpreting.

defined class Account
defined object Account

scala> 

注意:类和他的伴生对象可以相互访问私有成员,他们必须定义在同一个源文件中。

(5)独立对象 不与伴生类共享名称的单例对象称为独立对象。它可以用在很多地方,例如作为相关功能方法的工具类,或者定义Scala应用的入口点。

比如下面程序中的Demo就是独立对象,其中包含了main方法,类似与Java的主类中的main方法,是应用程序的入口。

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
   }
}

object Demo {
   def main(args: Array[String]) {
      val point = new Point(10, 20)
      printPoint

      def printPoint{
         println ("Point x location : " + point.x);
         println ("Point y location : " + point.y);
      }
   }
}

将上述程序保存在源文件:Demo.scala 中,使用Scala命令编译和执行此程序。

(6)apply方法 前面我们通过语句var a1 = Array("QQ", "Baidu", "Google")直接创建了数组,为什么不使用new呢,为什么不使用Array的构造器呢?类似的语句还很多,比如List("apples", "oranges", "pears")。 这是因为Scala在伴生对象中定义了apply方法,该方法返回的是伴生类的对象。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Account private(val newID:Int, newBalance:Double){
     private val id =newID;
     private var balance=newBalance
}
object Account {
     private var lastNumber = 0
     def newUniqueNumber() = { lastNumber += 1; lastNumber}
     def apply(newBalance:Double)=new Account(newUniqueNumber(),newBalance)
}
val act=Account(1000.0)

// Exiting paste mode, now interpreting.

defined class Account
defined object Account
act: Account = Account@6eac71db

scala> 

程序说明:

  • Account类的构造器是私有的,所以不能直接通过构造器来定义Account类的对象
  • Account类的伴生对象可以访问Account类的私有构造器, 通过apply方法定义了一个Account类的对象

注意:Array(100)和new Array(1000)是不同的。Array(100)是调用了伴生对象的apply方法,100是一个元素的值;而new Array(1000)是调用了Array类的辅助构造器this(100),创建了100个null元素。

2.14 包与引入

(1)默认引入的包 每个Scala程序默认从下面代码开始:

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

注意:java.lang包首先被引入;scala包的引入有些特殊,它允许覆盖之前的引入。比如scala.StringBuilder会覆盖java.lang.StringBuilder类;Predef对象也被引入, 它包含了很多实用方法。

2.15 继承

1、重写一个非抽象方法必须使用override修饰符。 2、只有主构造函数才可以往基类的构造函数里写参数。 3、在子类中重写超类的抽象方法时,你不需要使用override关键字。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc
   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点 : " + x);
      println ("y 的坐标点 : " + y);
   }
}

class Location(override val xc: Int, override val yc: Int,
   val zc :Int) extends Point(xc, yc){
   var z: Int = zc

   def move(dx: Int, dy: Int, dz: Int) {
      x = x + dx
      y = y + dy
      z = z + dz
      println ("x 的坐标点 : " + x);
      println ("y 的坐标点 : " + y);
      println ("z 的坐标点 : " + z);
   }
}

val loc = new Location(10, 20, 15);
loc.move(10, 10, 5);

// Exiting paste mode, now interpreting.

x 的坐标点 : 20
y 的坐标点 : 30
z 的坐标点 : 20
defined class Point
defined class Location
loc: Location = Location@1179731c

scala>

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏刘望舒

kotlin到底好在哪里?

最近在学kotlin,虽然还没有像其他博主一样立马就爱上它.但是不得不说,kotlin对比起java还是有不少优势的. 1、语法简洁 首先是语法比较简洁,能不简...

3437
来自专栏程序员宝库

了解 dubbo 序列化,从 bug 开始

首先交代背景,前几天遇到一个小bug,由于其他系统的一个DTO子类和父类有一个字段名重复了,所以导致我set的子类字段那边拿不到值。改起来很简单嘛,让对面把子类...

803
来自专栏逸鹏说道

Python3 与 C# 面向对象之~封装

在线编程:https://mybinder.org/v2/gh/lotapp/BaseCode/master 在线预览:http://github.lessch...

943
来自专栏Java进阶之路

提高java程序性能的小方法

1000
来自专栏MyBlog

Effective.Java 读书笔记(8)关于equals方法

重写equals看上去十分简单对吧,但是我觉得很多时候重写equals可能会招致一些问题,这些问题有时可能会特别严重,当然了不重写不就完事了吗?但是这只适用于那...

594
来自专栏james大数据架构

你真的会玩SQL吗?让人晕头转向的三值逻辑

你真的会玩SQL吗?系列目录 你真的会玩SQL吗?之逻辑查询处理阶段 你真的会玩SQL吗?和平大使 内连接、外连接 你真的会玩SQL吗?三范式、数据完整性 你真...

1788
来自专栏大内老A

WCF技术剖析之十二:数据契约(Data Contract)和数据契约序列化器(DataContractSerializer)

大部分的系统都是以数据为中心的(Data Central),功能的实现表现在对相关数据的正确处理。而数据本身,是有效信息的载体,在不同的环境具有不同的表示。一个...

3388
来自专栏青玉伏案

在Objective-C中浅谈面向对象

  接触面向对象也有一段时间了,当时是通过C++学习的OOP,后来又接触到了PHP和Java。每种OOP的语言在面向对象上或多或少都会有不同的地方,现在在学习O...

1726
来自专栏一“技”之长

JavaScript基础之六——内置对象 原

    JavaScript中的一些数据都是对象,对象实际上是属性与方法的包装。并不像其他类似Swift/OC/Java类的面向对象语言,在目前JavaScri...

752
来自专栏风口上的猪的文章

.NET面试题系列[11] - IEnumerable<T>的派生类

ICollection<T>继承IEnumerable<T>。在其基础上,增加了Add,Remove等方法,可以修改集合的内容。IEnumerable<T>的直...

652

扫码关注云+社区