美文网首页
《Scala程序设计(Ver.2)》读书笔记

《Scala程序设计(Ver.2)》读书笔记

作者: Azur_wxj | 来源:发表于2017-11-12 23:29 被阅读42次

第一章


  1. 命令行中使用:load命令来加载(编译并运行)文件(脚本文件):scala> :load example.scala
  2. 编译为字节码文件:
    • 脚本:将脚本内容封装入一个指定的类中,使用>scalac -Xscript MyClass example.scala于是会生成MyClass.class的文件。
    • 有package、类描述的代码文件:使用>scalac example.scala
  3. 对于生成的字节码文件,假定文件的包在com.example下,使用>scala -cp . com.example.MyClass来运行。
  4. 如果想要java来编译生成的scala的字节码文件,需要用到scala-library.jar文件,假定该文件在C:\Users\Berlin\.sbt\boot\scala-2.10.6\lib下,则写为>java -cp .;C:\Users\Berlin\.sbt\boot\scala-2.10.6\lib\scala-library.jar MyClass
  5. val foo定义一个不可变的量,var foo定义了一个可变的量。
  6. 类的定义:
    class Upper {
        def upper(strings: String*): Seq[String] = {
            strings.map((s:String) => s.toUpperCase())
        }
    }
    val up = new Upper
    println(up.upper("Hello", "World!"))
    
    • 定义了名为Upper的类,其中有名为upper的方法,
    • 方法接受String类型参数,参数名为strings,个数为任意个(因为String后有一个星号*)。
    • 返回类型为Seq泛型类型(序列),类型为String,相当于Java语法的Seq<String>
    • class Upper后没有参数列表(比如class Upper(name:String,age:Int){ def upper... }),因此构造方法没有参数,所以不用写括号,直接写val up = new Upper
  7. 方法定义
    def methodName( parameter1 : Type1,parameter2 : Type2 ) : returnedType = { //...method Body }
    • 返回类型通常可以省略(递归时要写)
    • 在方法体只有一个语句时可以省略花括号
    • 方法体最后一句表达式的值就是返回值。
    • 返回空可以写成...) : Unit = {//......}
  8. 单例对象
    object Upper {
        def upper(strings: String*) = strings.map(_.toUpperCase())
    }
    println(Upper.upper("Hello", "World!"))
    
    • object关键字定义了一个名为Upper的单例对象,
    • Scala 运行时只会创建Upper 的一个实例。也就是说,你无法通过new 创建Upper 对象。就好像Java 使用静态类型一样
    • 形如( foo : Type ) => foo.method()( foo : Type ) => method(a,foo,b,c)可以简写_.method()method(a,_,b,c)
  9. Scala 不支持静态类型
  10. 插值字符串:以s表示:println( s"Hello ${ this.name } "),则变量值会被替换进去。切记以s为标识
  11. case class:
    • 支持模式匹配,多用于模式匹配
    • 必须有参数列表,就算没有也要有括号:case class Clazz(){....
    • 参数列表中的参数为public,并且是val,即不可变,可以外部访问
    • case class创建实例时可以不用加new(普通class必须加new)
    • 默认实现了toString、hashCode、equals方法
    • 默认是可以序列化的,也就是实现了Serializable ;
    • 同时创建了伴生object,并实现apply方法
    • 更多参考...
  12. 伴生对象:case class会生成与其同名的单例对象(object),它实现了apply方法。这个方法是一个工厂方法,使用case class生成实例时不用new,就是因为scala可以自动寻找apply方法产生一个实例对象。因此,假设Point是一个case class,则这两句话是等价的:
    • val p1 = Point.apply(1.0, 2.0)
    • val p2 = Point(1.0, 2.0)
  13. 可以自己定义伴生对象。任何时候只要对象名(object Clazz {...})和类名(class Clazz {...})相同并且定义在同一个文件中,这些对象就能称作伴生对象。
    在伴生对象中可以定义自己的apply,然后使用类名(参数列表)即可使用
    object Singleton {
        def apply(age:Int,name:String):Unit =
            println(s"your name is ${name}, age is ${age}");
    }
    
    Singleton(name="Amy",age=100);
    
    //输出:your name ....
    
    但是如果这么写就会有问题:
    class CaseClass_{
        def apply(s:Int) ={println("good")}
    }
    CaseClass_(5)
    
    输出错误信息:error: not found: value CaseClass_
  14. equals方法:scala的==会映射为equals方法,即进行值比较(包括对象)。若比较对象的内存地址,则使用eqneobj1 eq obj2
  15. 嵌套导入:
    object Messages { 
        object Exit           // 没有类体
        object Finished
        case class Response(message: String) 
    }
    class ShapesDrawingActor { 
        import Messages._   //只在这个类范围内生效:导入Messages对象内容,可以直接使用,如Exit,而不用写全称Messages.Exit
        def ....
    
  16. 在Scala 中,main 方法必须为对象方法(object)。(在Java 中,main 方法必须是类静态方法:
     object Test{     //而不是 class Test,否则会提示 CaseClass_.main is not static
          def main(args: Array[String]) = {
                //.......
           }
      }
    

第二章


  1. 变量声明: val/var name : Type = value

    • 不可变:val array: Array[String] = new Array(5)
    • 可变:var stockPrice: Double = 100.0
      变量声明的同时必须立即初始化。(例外:如构造函数的参数列表:class Person(val name: String, var age: Int)
  2. Range: 支持Range 的类型包括Int、Long、Float、Double、Char、BigInt、BigDecimal

    • 1 to 10 : [1,10]
    • 1 until 10:[1,10)
    • 1 to 10 by 3
    • 10 to 1 by -3
    • 1L to 10L by 3
    • 'a' to 'g' by 3
    • BigDecimal(1.1) to BigDecimal(10.3) by 3.1
  3. 偏函数:在偏函数中只能使用case 语句(处理那些能与至少一个case 语句匹配的输入,输入却与所有语句都不匹配,系统就会抛出一个MatchError),而整个函数必须用花括号包围。

    object CaseClass_{
    def main(args: Array[String]) = {
            var func:PartialFunction[Any,String]={   //输入任意类型,返回字符串
                case s:String=>    //匹配String,值赋予s
                    "hello "+s
                case w:Int =>    //匹配Int,值赋予w
                    "This is Int"
                case whatAreYouTalking=>    //任意类型,赋予变量whatAreYouTalking
                    "Nothing" 
            }
            println(func("bbc"));
            println(func(123));
            println(func(3.14));
        }
    }
    

    输出:

    hello bbc
    This is Int
    Nothing
    

    在偏函数上调用isDefinedAt方法可以检测某个实例是否能被该偏函数匹配,返回是布尔值:func.isDefinedAt(3.14f)

  4. copy 方法:copy 方法也是case 类自动创建的。copy 方法允许你在创建case 类的新实例时只给出与原对象不同部分的参数。例如某case class方法的参数列表有x、y两个值且都有默认值,则调用copy方法时只写 p.copy(y=3.14),从而创建一个新的实例,它的y是3.14但是x是默认值。

  5. 方法具有多个参数列表

    • def draw (offset: Point = Point(0, 0)) (f: String => Unit) = {....} 有两个参数列表
    • 使用方法:draw(Point(1, 2))(str => println("hello")
    • 允许把参数列表两边的圆括号替换为花括号: draw(Point(1, 2)){str => println("hello")}
    • 使用缺省的参数,第一个圆括号就不能省略:draw(){str => println("hello")}
    • 请注意区分函数体和参数列表,尽管在scala中它们都可用花括号包裹。
  6. 注意无论是不是多参数列表,只有为单一参数时才能大小括号混用,否则只能用小括号:

    scala> def s(a:Int)(b:String,c:Double)={
     | println(a)
     | println(b,c)
     | }
     scala> s{123}{"das",3.14} //因为第二个列表不是单参数,所以不能用花括号。
     <console>:1: error: ';' expected but ',' found.
       s{123}{"das",3.14}
                   ^
    

    参考: Scala之小括号和花括号(Parentheses & Crurly Braces)

  7. 多参数列表可以进行类型推断:

    • def m1(a: Int, f : Int => String) = .....,则m1(100, i => s"$i + $i")会提示i的类型没有给定
    • def m2(a: Int)(f: Int => String) = ...,则m2(100)(i => s"$i + $i")就米有错,Scala可以推断出i是Int类型。
  8. 方法的定义还可以嵌套:

    def outer() = {
        def inner() : Int ={ 
            ...
        }
        inner();
    }
    

    内部参数可以屏蔽同名外部参数

  9. 使用scala.annotation.tailrec的tailrec注解来检查递归函数是否实现了尾递归,如果没有则会抛出异常:

    import scala.annotation.tailrec
    @tailrec
    def method(...) : Type = { ...}
    
  10. 推断类型信息

    • 在java等静态语言中,要写:HashMap<Integer, String> intToStringMap = new HashMap<Integer, String>();或者HashMap<Integer, String> intToStringMap = new HashMap<>();
    • 在Scala中只用写:
      • val intToStringMap: HashMap[Integer, String] = new HashMap
      • val intToStringMap2 = new HashMap[Integer, String] 非显式类型注解
    • 例如,
      def report(name:String)={
          val copy = name;   
          // copy没有写成 var copy:String =name。因为可以自动推断出类型
          println(s"Your name is ${copy}")
      }
      report("Robust") //输出:Your name is Robust
      
      但是如果写成
      def report(name:String)={
         var copy :String;  // 或是 var copy,即不指定类型,但是都没有初始化
         copy = name;   
         println(s"Your name is ${copy}")
      }
      report("Robust") //输出:Your name is Robust
      
      则会报错:
      • var copy;error: '=' expected but ';' found.
      • var copy :String;error: only traits and abstract classes can have declared but undefined members
  11. 需要显式类型注解的情况

    • 在类中抽象声明时,声明了可变的var 变量或不可变的val 变量,但没有进行初始化。
    • 所有的方法参数(如def deposit(amount: Money) = {…})。注意,如果写成def deposit(var amount: Money) = ...def deposit(val amount: Money) = ...就会报出两个错误:
      • error: identifier expected but 'var' found.
      • error: only traits and abstract classes can have declared but undefined members
        因此在方法的参数列表中不要写val或var
    • 方法的返回值类型,在以下情况中必须显式声明其类型:
      • 明显地使用了return
      • 递归方法
      • 两个或多个方法重载(拥有相同的函数名),其中一个方法调用了另一个重载方法,调用者需要显式类型注解。
      • Scala 推断出的类型比你期望的类型更为宽泛,如Any。
  12. Scala中下划线的用法

  13. _*的用法:设def joiner(strings: String*): String = {....}函数joiner是一个接受变长参数的函数,参数类型为String。则现在有一个列表strings: List[String],为了将其变为分隔的变长参数从而可以适应joiner的参数列表,可以使用:joiner ( strings: _*)的语法结构。这个可以这么来理解:

    • 变量标识符后面的冒号表示告诉编译器这个变量是某种类型
    • 下划线表示类型却不是指定的,而是根据输入推断得出的。Scala 不允许你写成strings :String *即使你需要为第一个joiner 方法指定的输入类型就是String。(奇怪)
    • *指示是一个变长列表
      所以综上所述,它就是告诉编译器这个参数需要由列表类型“拆分”成变长参数列表。
  14. 返回类型推断:最近公共类型。假定某方法不指定返回值,其中有一个if-else结构,if中返回List[Int]类型结果,而else返回List[String]结果,则Scala推断出的返回值类型就是它们的公共父类型,即List[Any]

  15. 函数和过程:在scala中能够定义函数。定义的函数可以有返回值,也可以没有返回值。没有返回值的叫做过程,有返回值的叫做函数。在语法上的区别是是否有等号

    • def greeting(name:String){ println(s"Hello ${name}"); 3.14159} 这是一个过程,因为参数列表和花括号之间没有等号,所以它返回的是Unit尽管它会返回一个浮点数3.14159。打印它的结果是不可预知的,通常情况会打印一个()。例如,println(greeting("David"))会输出Hello David以及()
    • def greeting(name:String)={ println(s"Hello ${name}")} 这是一个函数,尽管类型推断认为它也返回Unit,并且println(greeting("David"))输出结果和上面相同。
  16. break 和continue在Scala 中不存在

  17. 若方法中含有某些关键字,则使用单引号来表示。比如,java.util.Scanner.match,而match是Scala关键字,所以要写java.util.Scanner.`match`

相关文章

网友评论

      本文标题:《Scala程序设计(Ver.2)》读书笔记

      本文链接:https://www.haomeiwen.com/subject/ijsgmxtx.html