zoukankan      html  css  js  c++  java
  • Scala 随笔

    转自:http://dblab.xmu.edu.cn/blog/spark/

    一、声名值和变量

    1. val变量

    1.1 隐式声明变量

    scala> val myStr = "Hello World!"
    myStr: String = Hello World!

    1.2 显式声明变量的类型

    scala> val myStr2 : String = "Hello World!"
    myStr2: String = Hello World!

    1.3 使用java.lang.String来声明

    scala> val myStr3 : java.lang.String = "Hello World!"
    myStr3: String = Hello World!

    2. var变量

    2.1 声明变量

    scala> var myPrice : Double = 9.9
    myPrice: Double = 9.9

    再次对myPrice进行赋值

    scala> myPrice = 10.6
    myPrice: Double = 10.6

    二、基本数据类型和操作

    1. 基本数据类型

    Scala的数据类型包括:Byte、Char、Short、Int、Long、Float、Double和Boolean。和Java不同的是,在Scala中,这些类型都是“类”,并且都是包scala的成员,比如,Int的全名是scala.Int。对于字符串,Scala用java.lang.String类来表示字符串。

    val i = 123  //123就是整数字面量
    val i = 3.14 //3.14就是浮点数字面量
    val i = true //true就是布尔型字面量
    val i = 'A' //'A'就是字符字面量
    val i = "Hello" //"Hello"就是字符串字面量

    2. 操作符

    a 方法 b

    a.方法(b)

    示例:

    scala> val sum1 = 5 + 3 //实际上调用了 (5).+(3)
    sum1: Int = 8
    scala> val sum2 = (5).+(3) //可以发现,写成方法调用的形式,和上面得到相同的结果
    sum2: Int = 8

    和Java不同,在Scala中并没有提供++和–操作符

    scala> var i = 5;
    i: Int = 5
    scala> i += 1  //将i递增
    scala> println(i)
    6

    三、Range

    在执行for循环时,我们经常会用到数值序列,比如,i的值从1循环到5,这时就可以采用Range来实现。

    1. to

    (1)创建一个从1到5的数值序列,包含区间终点5,步长为1

    scala> 1 to 5
    res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)

    // 或

    scala> 1.to(5)
    res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)

    (2)创建一个从1到10的数值序列,包含区间终点10,步长为2

    scala> 1 to 10 by 2
    res2: scala.collection.immutable.Range = Range(1, 3, 5, 7, 9)

    2. until

    (1)创建一个从1到5的数值序列,不包含区间终点5,步长为1

    scala> 1 until 5
    res1: scala.collection.immutable.Range = Range(1, 2, 3, 4)

    (2)创建一个从1到5的数值序列,不包含区间终点5,步长为2

    scala> 1 until 5 to 2
    res1: scala.collection.immutable.Range = Range(1, 3)

    四、打印语句

    1. print

    print("My name is:")
    print("Ziyu")

    2. printf

    val i = 5;
    val j = 8;
    printf("My name is %s. I hava %d apples and %d eggs.
    ","Ziyu",i,j)

    五、读写文件

    1.写入文本文件

    scala> import java.io.PrintWriter
    import java.io.PrintWriter //这行是Scala解释器执行上面语句后返回的结果
    scala> val out = new PrintWriter("output.txt")
    out: java.io.PrintWriter = java.io.PrintWriter@25641d39  //这行是Scala解释器执行上面语句后返回的结果
    scala> for (i <- 1 to 5) out.println(i)
    scala> out.close()

    2. 读取文本文件中的行

    scala> import scala.io.Source
    import scala.io.Source //这行是Scala解释器执行上面语句后返回的结果
    scala> val inputFile = Source.fromFile("output.txt")
    inputFile: scala.io.BufferedSource = non-empty iterator  //这行是Scala解释器执行上面语句后返回的结果
    scala> val lines = inputFile.getLines //返回的结果是一个迭代器
    lines: Iterator[String] = non-empty iterator  //这行是Scala解释器执行上面语句后返回的结果
    scala> for (line <- lines) println(line)

    六、条件控制

    1.if

    和Java一样,if语句可以采用各种嵌套的形式,比如:

    val x = 3
    if (x>0) {
        println("This is a positive number")
    } else if (x==0) {
        println("This is a zero")
    } else {
        println("This is a negative number")
    }

    但是,有一点与Java不同的是,Scala中的if表达式的值可以赋值给变量,比如:

    val x = 6
    val a = if (x>0) 1 else -1

    七、循环

    1. while

    var i = 9
    while (i > 0) {
        i -= 1
        printf("i is %d
    ",i)
    }

    2. do-while

    var i = 0
    do {
        i += 1
        println(i)
    }while (i<5)

    3. for

    for (i <- 1 to 5) println(i)

    设置步长为2,如下所示:

    for (i <- 1 to 5 by 2) println(i)

    比如,我们只希望输出1到5之中的所有偶数,就需要使用到称为“守卫(guard)”的表达式,可以采用以下语句:

    for (i <- 1 to 5 if i%2==0) println(i)

    Scala也支持“多个生成器”的情形,可以用分号把它们隔开,比如:

    for (i <- 1 to 5; j <- 1 to 3) println(i*j)

    也可以给每个生成器都添加一个“守卫”,如下:

    for (i <- 1 to 5 if i%2==0; j <- 1 to 3 if j!=i) println(i*j)

    八、数据结构

    1. 数组(Array)

    1.1 声明定长数组

    定长数组,就是长度不变的数组,在Scala中使用Array进行声明

    val intValueArr = new Array[Int](3)  //声明一个长度为3的整型数组,每个数组元素初始化为0
    intValueArr(0) = 12 //给第1个数组元素赋值为12
    intValueArr(1) = 45  //给第2个数组元素赋值为45
    intValueArr(2) = 33 //给第3个数组元素赋值为33

    实际上,Scala提供了更加简洁的数组声明和初始化方法,如下:

    val intValueArr = Array(12,45,33)
    val myStrArr = Array("BigData","Hadoop","Spark")

    2. 列表(List)

    2.1 声明list

    val intList = List(1,2,3)

    2.2 ::

    由于列表的头部是一个元素,所以,我们可以使用::操作,在列表的头部增加新的元素,得到一个新的列表.

    val intList = List(1,2,3)
    val intListOther = 0::intList

    也可以:

    val intList = 1::2::3::Nil

    2.3 function

    Scala还为列表提供了一些常用的方法,比如,如果要实现求和,可以直接调用sum方法,如下:

    intList.sum

    3.元组(tuple)

    元组是不同类型的值的聚集。元组和列表不同,列表中各个元素必须是相同类型,而元组可以包含不同类型的元素。

    3.1 声明一个名称为tuple的元组

    scala> val tuple = ("BigData",2015,45.0)
    tuple: (String, Int, Double) = (BigData,2015,45.0)  //这行是Scala解释器返回的执行结果
    scala> println(tuple._1)
    BigData
    scala> println(tuple._2)
    2015
    scala> println(tuple._3)
    45.0

    4. 集(Set)

    4.1 不可变集

    scala> var mySet = Set("Hadoop","Spark")
    mySet: scala.collection.immutable.Set[String] = Set(Hadoop, Spark)
    scala> mySet += "Scala"  //向mySet中增加新的元素
    scala> println(mySet.contains("Scala"))
    true

    4.2 可变集

    scala> import scala.collection.mutable.Set
    import scala.collection.mutable.Set
    
    scala> val myMutableSet = Set("Database","BigData")
    myMutableSet: scala.collection.mutable.Set[String] = Set(BigData, Database)
    
    scala> myMutableSet += "Cloud Computing"
    res0: myMutableSet.type = Set(BigData, Cloud Computing, Database)
    
    scala> println(myMutableSet)
    Set(BigData, Cloud Computing, Database)

    5. 映射(Map)

    5.1 不可变映射

    val university = Map("XMU" -> "Xiamen University", "THU" -> "Tsinghua University","PKU"->"Peking University")

    获取value

    println(university("XMU"))

    如果要检查映射中是否包含某个值,可以使用contains方法

    val xmu = if (university.contains("XMU")) university("XMU") else 0
    println(xmu)

    5.2 可变映射

    import scala.collection.mutable.Map
    val university2 = Map("XMU" -> "Xiamen University", "THU" -> "Tsinghua University","PKU"->"Peking University")
    university2("XMU") = "Ximan University" //更新已有元素的值
    university2("FZU") = "Fuzhou University" //添加新元素

    也可以使用+=操作来添加新的元素,如下:

    university2 + = ("TJU"->"Tianjin University") //添加一个新元素
    university2 + = ("SDU"->"Shandong University","WHU"->"Wuhan University") //同时添加两个新元素

    5.3 循环遍历

    for ((k,v) <- 映射) 语句块

    下面给出一个实例:

    for ((k,v) <- university) printf("Code is : %s and name is: %s
    ",k,v)

    或者

    val map = Map("zhangsan" -> 18, "li" -> 20, "wangwu" -> 23)
       
        for (i <- map) {
          println(i._1 + " ---- " + i._2)
        }

    6. 迭代器(Iterator)

    通常可以通过while循环或者for循环实现对迭代器的遍历。
    while循环如下:

    val iter = Iterator("Hadoop","Spark","Scala")
    while (iter.hasNext) {
        println(iter.next())
    }

    for循环如下:

    val iter = Iterator("Hadoop","Spark","Scala")
    for (elem <- iter) {
        println(elem)
    }

    九、类

    1. class类

    最简单的类的定义形式是:

    class Counter{
         //这里定义类的字段和方法
    }

    然后,就可以使用new关键字来生成对象:

    new Counter //或者new Counter()

    2. 给类增加字段和方法

    下面我们给这个类增加字段和方法:

    class Counter {
        private var value = 0
        def increment(): Unit = { value += 1}
        def current(): Int = {value}
    }

    3. 创建对象

    下面我们新建对象,并调用其中的方法:

    val myCounter = new Counter
    myCounter.increment() //或者也可以不用圆括号,写成myCounter.increment
    println(myCounter.current)

    4. getter和setter方法

    class Counter {
        var value = 0 //注意这里没有private修饰符,从而让这个变量对外部可见
        def increment(step: Int): Unit = { value += step}
        def current(): Int = {value}
    }
    object MyCounter{
        def main(args:Array[String]){
            val myCounter = new Counter
            println(myCounter.value)  //不是用getXxx获取字段的值
            myCounter.value = 3 //不是用setXxx设置字段的值
            myCounter.increment(1) //这里设置步长为1,每次增加1
            println(myCounter.current)
        }
    }

    在Scala中,可以通过定义类似getter和setter的方法,分别叫做value和value_=,具体如下:

    class Counter {
        private var privateValue = 0  //变成私有字段,并且修改字段名称
        def value = privateValue //定义一个方法,方法的名称就是原来我们想要的字段的名称
        def value_=(newValue: Int){
            if (newValue > 0) privateValue = newValue //只有提供的新值是正数,才允许修改
        }
        def increment(step: Int): Unit = { value += step}
        def current(): Int = {value}
    }
    object MyCounter{
        def main(args:Array[String]){
            val myCounter = new Counter
            println(myCounter.value)  //打印value的初始值
            myCounter.value = 3 //为value设置新的值
            println(myCounter.value)  //打印value的新值 
            myCounter.increment(1) //这里设置步长为1,每次增加1
            println(myCounter.current)
        }
    }

    5. 辅助构造器

    class Counter {
        private var value = 0 //value用来存储计数器的起始值
        private var name = "" //表示计数器的名称
        private var mode = 1 //mode用来表示计数器类型(比如,1表示步数计数器,2表示时间计数器)
        def this(name: String){ //第一个辅助构造器
            this() //调用主构造器
            this.name = name
        }
        def this (name: String, mode: Int){ //第二个辅助构造器
            this(name) //调用前一个辅助构造器
            this.mode = mode
        }
        def increment(step: Int): Unit = { value += step}
        def current(): Int = {value}
        def info(): Unit = {printf("Name:%s and mode is %d
    ",name,mode)}
    }
    object MyCounter{
        def main(args:Array[String]){
            val myCounter1 = new Counter  //主构造器
            val myCounter2 = new Counter("Runner") //第一个辅助构造器,计数器的名称设置为Runner,用来计算跑步步数
            val myCounter3 = new Counter("Timer",2) //第二个辅助构造器,计数器的名称设置为Timer,用来计算秒数
            myCounter1.info  //显示计数器信息
            myCounter1.increment(1)     //设置步长  
            printf("Current Value is: %d
    ",myCounter1.current) //显示计数器当前值
            myCounter2.info  //显示计数器信息
            myCounter2.increment(2)     //设置步长  
            printf("Current Value is: %d
    ",myCounter2.current) //显示计数器当前值
            myCounter3.info  //显示计数器信息
            myCounter3.increment(3)     //设置步长  
            printf("Current Value is: %d
    ",myCounter3.current) //显示计数器当前值
    
        }
    }
    编译执行上述代码后,得到如下结果:
    
    Name: and mode is 1
    Current Value is: 1
    Name:Runner and mode is 1
    Current Value is: 2
    Name:Timer and mode is 2
    Current Value is: 3
    主构造器
    Scala的每个类都有主构造器。但是,Scala的主构造器和Java有着明显的不同,Scala的主构造器是整个类体,需要在类名称后面罗列出构造器所需的所有参数,这些参数被编译成字段,字段的值就是创建对象时传入的参数的值。
    对于上面给计数器设置name和mode的例子,刚才我们是使用辅助构造器来对name和mode的值进行设置,现在我们重新来一次,这次我们转而采用主构造器来设置name和mode的值。
    
    class Counter(val name: String, val mode: Int) {
        private var value = 0 //value用来存储计数器的起始值    
        def increment(step: Int): Unit = { value += step}
        def current(): Int = {value}
        def info(): Unit = {printf("Name:%s and mode is %d
    ",name,mode)}
    }
    object MyCounter{
        def main(args:Array[String]){       
            val myCounter = new Counter("Timer",2)
            myCounter.info  //显示计数器信息
            myCounter.increment(1)  //设置步长  
            printf("Current Value is: %d
    ",myCounter.current) //显示计数器当前值       
        }
    }

    十、对象

    1.单例对象

    下面是单例对象的定义:

    object Person {
        private var lastId = 0  //一个人的身份编号
        def newPersonId() = {
            lastId +=1
            lastId
        }
    }

    2. 伴生对象

    在Java中,我们经常需要用到同时包含实例方法和静态方法的类,在Scala中可以通过伴生对象来实现。当单例对象与某个类具有相同的名称时,它被称为这个类的“伴生对象”。类和它的伴生对象必须存在于同一个文件中,而且可以相互访问私有成员(字段和方法)。

    class Person {
        private val id = Person.newPersonId() //调用了伴生对象中的方法
        private var name = ""
        def this(name: String) {
            this()
            this.name = name
        }
        def info() { printf("The id of %s is %d.
    ",name,id)}
    }
    object Person {
        private var lastId = 0  //一个人的身份编号
        private def newPersonId() = {
            lastId +=1
            lastId
        }
        def main(args: Array[String]){
            val person1 = new Person("Ziyu")
            val person2 = new Person("Minxing")
            person1.info()
            person2.info()      
        }
    }

    伴生对象中定义的newPersonId()实际上就实现了Java中静态(static)方法的功能

    实际上,在编译上面的源代码文件以后,在Scala里面的class和object在Java层面都会被合二为一,class里面的成员成了实例成员,object成员成了static成员。并且伴生对象Person的定义的方法不能用private修饰符,必须去掉,因为如果不去掉,作为伴生对象的私有方法,在javap反编译后,在执行结果中是看不到这个方法的

    3. apply方法和update方法

    object apply 是一种比较普遍用法。 主要用来解决复杂对象的初始化问题,同时也是单例.

    class TestApplyClass {
    
        def apply(param: String): String = {
    
            println("apply method called, parameter is: " + param)
    
            "Hello World!"
        }
    }
    val myObject = new TestApplyClass
    println(myObject("param1"))

    运行后会得到以下结果:

    apply method is called, parameter is:param1
    Hello World!

    下面我们测试一个伴生类和伴生对象中的apply方法实例。请在Linux系统的“/usr/local/scala/mycode/test.scala”文件中输入以下代码:

    class TestApplyClassAndObject {
    }
    class ApplyTest{
        def apply() = println("apply method in class is called!")
        def greetingOfClass: Unit ={
            println("Greeting method in class is called.")
        }
    }
    object ApplyTest{
         def apply() = {
              println("apply method in object is called")
            new ApplyTest()
         }
    }
    object  TestApplyClassAndObject{
         def main (args: Array[String]) {       
            val a = ApplyTest() //这里会调用伴生对象中的apply方法       
            a.greetingOfClass
            a() // 这里会调用伴生类中的apply方法         
        }
    }

    上述代码执行后得到以下结果:

    apply method in object is called
    Greeting method in class is called.
    apply method in class is called!

    我们通常将伴生对象作为工厂使用,这样就不需要使用关键字new来创建一个实例化对象了,具体实例如下:

    class Car(name: String){
        def info() {println("Car name is "+ name)}
    }
    object Car {
      def apply(name: String) = new Car(name) //apply方法会调用伴生类Car的构造方法创建一个Car类的实例化对象
    }
    object MyTest{
         def main (args: Array[String]) {       
            val mycar = Car("BMW") //这里会调用伴生对象中的apply方法,apply方法会创建一个Car类的实例化对象
                    mycar.info()
        }
    }

    上述代码执行后得到以下结果:

    Car name is BMW

    十一、继承

    Scala中的继承与Java有着显著的不同:
    (1)重写一个非抽象方法必须使用override修饰符。
    (2)只有主构造器可以调用超类的主构造器。
    (3)在子类中重写超类的抽象方法时,不需要使用override关键字。
    (4)可以重写超类中的字段。

    1.抽象类

    以汽车为例子,首先我们创建一个抽象类,让这个抽象类被其他类继承。

    abstract class Car{   //是抽象类,不能直接被实例化
       val carBrand: String //字段没有初始化值,就是一个抽象字段
         def info() //抽象方法,不需要使用abstract关键字
         def greeting() {println("Welcome to my car!")}
    }

    关于上面的定义,说明几点:
    (1)定义一个抽象类,需要使用关键字abstract。
    (2)定义一个抽象类的抽象方法,也不需要关键字abstract,只要把方法体空着,不写方法体就可以。
    (3)抽象类中定义的字段,只要没有给出初始化值,就表示是一个抽象字段,但是,抽象字段必须要声明类型,比如:val carBrand: String,就把carBrand声明为字符串类型,这个时候,不能省略类型,否则编译会报错。

    2.扩展类

     抽象类不能直接被实例化,所以,下面我们定义几个扩展类,它们都是扩展了Car类,或者说继承自Car类。

    class BMWCar extends Car {
        override val carBrand = "BMW"  //重写超类字段,需要使用override关键字,否则编译会报错
        def info() {printf("This is a %s car. It is on sale", carBrand)} //重写超类的抽象方法时,不需要使用override关键字,不过,如果加上override编译也不错报错
        override def greeting() {println("Welcome to my BMW car!")} //重写超类的非抽象方法,必须使用override关键字
    }
    
    class BYDCar extends Car { 
        override val carBrand = "BYD" //重写超类字段,需要使用override关键字,否则编译会报错
        def info() {printf("This is a %s car. It is cheap.", carBrand)} //重写超类的抽象方法时,不需要使用override关键字,不过,如果加上override编译也不错报错
        override def greeting() {println("Welcome to my BYD car!")} //重写超类的非抽象方法,必须使用override关键字
    }

    在test.scala文件中输入以下内容:

    abstract class Car{
       val carBrand: String 
         def info()
         def greeting() {println("Welcome to my car!")}
    }
    class BMWCar extends Car {
        override val carBrand = "BMW"
        def info() {printf("This is a %s car. It is expensive.
    ", carBrand)}
        override def greeting() {println("Welcome to my BMW car!")}
    }
    
    class BYDCar extends Car {
        override val carBrand = "BYD" 
        def info() {printf("This is a %s car. It is cheap.
    ", carBrand)}
        override def greeting() {println("Welcome to my BYD car!")}
    }
    
    object MyCar {  
        def main(args: Array[String]){
            val myCar1 = new BMWCar()
            val myCar2 = new BYDCar()
            myCar1.greeting()
            myCar1.info()       
            myCar2.greeting()
            myCar2.info()
        }
    }

    十二、特质(trait)

    1. 特质的定义

    特质的定义和类的定义非常相似,有区别的是,特质定义使用关键字trait。

    trait CarId{
      var id: Int
        def currentId(): Int     //定义了一个抽象方法
    }

    上面定义了一个特质,里面包含一个抽象字段id和抽象方法currentId。注意,抽象方法不需要使用abstract关键字,特质中没有方法体的方法,默认就是抽象方法。

     

    2.把特质混入类中

    特质定义好以后,就可以使用extends或with关键字把特质混入类中。

    class BYDCarId extends CarId{ //使用extends关键字
       override var id = 10000 //BYD汽车编号从10000开始
         def currentId(): Int = {id += 1; id} //返回汽车编号
     }
     class BMWCarId extends CarId{ //使用extends关键字
       override var id = 20000 //BMW汽车编号从20000开始
         def currentId(): Int = {id += 1; id} //返回汽车编号
     } 

    在test.scala文件中输入以下内容:

    trait CarId{
      var id: Int
        def currentId(): Int     //定义了一个抽象方法
    }
    class BYDCarId extends CarId{ //使用extends关键字
       override var id = 10000 //BYD汽车编号从10000开始
         def currentId(): Int = {id += 1; id} //返回汽车编号
     }
     class BMWCarId extends CarId{ //使用extends关键字
       override var id = 20000 //BMW汽车编号从10000开始
         def currentId(): Int = {id += 1; id} //返回汽车编号
     } 
     object MyCar { 
        def main(args: Array[String]){
            val myCarId1 = new BYDCarId()       
            val myCarId2 = new BMWCarId()
            printf("My first CarId is %d.
    ",myCarId1.currentId)
            printf("My second CarId is %d.
    ",myCarId2.currentId)
        }
    }

    3.特质可以包含具体实现

    上面的实例中,特质只包含了抽象字段和抽象方法,相当于实现了类似Java接口的功能。实际上,特质也可以包含具体实现,也就是说,特质中的字段和方法不一定要是抽象的。

    trait CarGreeting{
      def greeting(msg: String) {println(msg)}  
    }

    4.把多个特质混入类中

    在test.scala文件中输入以下内容:

    trait CarId{
      var id: Int
        def currentId(): Int     //定义了一个抽象方法
    }
    trait CarGreeting{
      def greeting(msg: String) {println(msg)}  
    }
    
    class BYDCarId extends CarId with CarGreeting{ //使用extends关键字混入第1个特质,后面可以反复使用with关键字混入更多特质
       override var id = 10000 //BYD汽车编号从10000开始
         def currentId(): Int = {id += 1; id} //返回汽车编号
     }
     class BMWCarId extends CarId with CarGreeting{ //使用extends关键字混入第1个特质,后面可以反复使用with关键字混入更多特质
       override var id = 20000 //BMW汽车编号从10000开始
         def currentId(): Int = {id += 1; id} //返回汽车编号
     } 
     object MyCar { 
        def main(args: Array[String]){
            val myCarId1 = new BYDCarId()       
            val myCarId2 = new BMWCarId()
            myCarId1.greeting("Welcome my first car.")
            printf("My first CarId is %d.
    ",myCarId1.currentId)        
            myCarId2.greeting("Welcome my second car.")
            printf("My second CarId is %d.
    ",myCarId2.currentId)
        }
    }

    上面命令执行后,会在屏幕输出以下结果:

    Welcome my first car.
    My first CarId is 10001.
    Welcome my second car.
    My second CarId is 20001.

     ///

  • 相关阅读:
    Google Map API使用详解(三)——Google Map基本常识(上)
    Google Map API使用详解(十)——使用JavaScript创建地图详解(上)
    sethc.exe
    taobao_java
    "void __cdecl operator delete[](void *)" (??_V@YAXPAX@Z) 已经在 LIBCMTD.lib(delete2.obj) 中定义 错误
    some Content
    变参 C++ 转帖
    阅读大型程序你得到什么
    一些模块
    a common jsp
  • 原文地址:https://www.cnblogs.com/erlou96/p/14365589.html
Copyright © 2011-2022 走看看