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

第一章


  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`

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,265评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,078评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,852评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,408评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,445评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,772评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,921评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,688评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,130评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,467评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,617评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,276评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,882评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,740评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,967评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,315评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,486评论 2 348

推荐阅读更多精彩内容