美文网首页
《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