类
类的定义
![](https://i-blog.csdnimg.cn/blog_migrate/0305a9e006e8e87a84f17ff9ce3d7821.png)
字段定义:用val或var关键字进行定义
方法定义:
![](https://i-blog.csdnimg.cn/blog_migrate/15127465d0eb0c933c192be84b53a805.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9493721ea694e036d574a0d148a2e481.png)
使用new关键字创建一个类的实例
![](https://i-blog.csdnimg.cn/blog_migrate/d0ecb1808f2b92538984f4fc36a26748.png)
类成员可见性
Scala类中所有成员的默认可见性为公有,任何作用域内都能直接访问公有成员
除了默认的公有可见性,Scala也提供private和protected
其中,private成员只对本类型和嵌套类型可见;protected成员对本类型和其继承类型都可见
为了避免直接暴露public字段,建议将字段设置为private,对于private字段,Scala采用类似Java中的getter和setter方法,定义了两个成对的方法value和value_=进行读取和修改
![](https://i-blog.csdnimg.cn/blog_migrate/bdcac52f49aae8a1c18bbc3d700e7ae5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/625d172c279b7c9872c9b38ae0e7d5be.png)
Scala语法中有如下规范,当编译器看到以value和value_=这种成对形式出现的方法时,它允许用户去掉下划线_,而采用类似赋值表达式的形式
![](https://i-blog.csdnimg.cn/blog_migrate/fdc19ccb6024ec6e4e9f9be396aad919.png)
方法的定义方式
基本语法:
![](https://i-blog.csdnimg.cn/blog_migrate/6b39daee25efd573f8686527ba038069.png)
方法参数前不能加.上val或var,所有的方法参数都是不可变类型
无参数的方法定义时可以省略括号,这时调用时也不能带有括号;如果
定义时带有括号,则调用时可以带括号,也可以不带括号
方法名后面的圆括号()可以用大括号{}来代替
如果方法只有一个参数,可以省略点号(.)而采用中缀操作符调用方法
如果方法体只有一条语句,可以省略方法体两边的大括号
![](https://i-blog.csdnimg.cn/blog_migrate/bbbd2eca8677cd67dfabc7d239b3991c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/61d0c8b3da7aa00cdffeb91bcf3ccedd.png)
当方法的返回结果类型可以从最后的表达式推断出时,可以省略结果类型
如果方法返回类型为Unit,可以同时省略返回结果类型和等号,但不能省略大括号
![](https://i-blog.csdnimg.cn/blog_migrate/a99ddc01c23f904775d221de59fe9965.png)
构造器
Scala类的定义主体就是类的构造器,称为主构造器。在类名之后用圆括号列出主构造器的参数列表
主构造器的参数前可以使用val或var关键字
Scala内部将自动为这些参数创建私有字段,并提供对应的访问方法
![](https://i-blog.csdnimg.cn/blog_migrate/c7fe630c6eb431fa64a6485073b0fea3.png)
·如果不希望将构造器参数成为类的字段,只需要省略关键字var或者val
Scala类可以包含零个或多个辅助构造器(( auxiliaryconstructor) 。
辅助构造器使用this进行定义,this的返回类型为Unit
每个辅助构造器的第一个表达式必须是调用一个此前已经定义的辅助构造器或主构造器,调用的形式为“this(参数列表)”
![](https://i-blog.csdnimg.cn/blog_migrate/cab05ee35d1919f1a16a2b5c5d122be4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/adcd3d9827b704f9d0f2ccae940b5d60.png)
对象
单例对象
Scala采用单例对象( singleton object)来实现与Java静态成员同样的功能
使用object关键字定义单例对象
![](https://i-blog.csdnimg.cn/blog_migrate/f9f681303f57e91098f05bad2c9924f5.png)
单例对象的使用与一个普通的类实例一样:
![](https://i-blog.csdnimg.cn/blog_migrate/a8f218b08d745d30cfec2702877d85b7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4de3f7f5e6286d231037ee7cfad4933c.png)
伴生对象和孤立对象
当一个单例对象和它的同名类一起出现时,这时的单例对象被称为这个同名类的“伴生对象”(companionobject)相应的类被称为这个单例对象的“伴生类”。
类和它的伴生对象必须存在于同一个文件中,可以相互访问私有成员
没有同名类的单例对象,被称为孤立对象(standaloneobject)。
一般情况下,Scala程序的入口点main方法就是定义在一个孤立对象里
![](https://i-blog.csdnimg.cn/blog_migrate/9a18e639821f5fb379f15a87cf83f51b.png)
apply方法
思考下行代码的执行过程:
![](https://i-blog.csdnimg.cn/blog_migrate/9c3aba811c7a75c6a6a5f04ce5fca4ce.png)
Scala自动调用Array类的伴生对象Array中的一个称为apply的方法,来创建一个Array对象myStrArr
apply方法调用约定:用括号传递给类实例或单例对象名一个或多个参数时,Scala会在相应的类或对象中查找方法名为apply且参数列表与传入的参数一致的方法,并用传入的参数来调用该apply方法
![](https://i-blog.csdnimg.cn/blog_migrate/54117f6463547724b2de9cbe1c11b042.png)
![](https://i-blog.csdnimg.cn/blog_migrate/72ae0daf8e0fbf88f12258a7d7fea8ea.png)
伴生对象中的apply方法:将所有类的构造方法以apply方法的形式定义在伴生对象中,这样伴生对象就像生成类实例的工厂,而这些apply方法也被称为工厂方法
![](https://i-blog.csdnimg.cn/blog_migrate/bbd04438f22b2e99fa0588a90eb31769.png)
为什么要设计apply方法?
保持对象和函数之间使用的一致性
面向对象:“对象.方法”VS 数学:“函数(参数)”
Scala中一切都是对象,包括函数也是对象。Scala中的函数既保留括号调用样式,也可以使用点号调用形式,其对应的方法名即为apply
![](https://i-blog.csdnimg.cn/blog_migrate/1e4ebe4aca874a2db6da93d3be06e42c.png)
为什么要设计apply方法?
Scala的对象也可以看成函数,前提是该对象提供了apply方法
![](https://i-blog.csdnimg.cn/blog_migrate/7040508933f5a8b09ab41387edbb0fb1.png)
update方法
与apply方法类似的update方法也遵循相应的调用约定:
当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的update方法,并将括号里的参数和等号右边的值一起作为update方法的输入参数来执行调用
![](https://i-blog.csdnimg.cn/blog_migrate/121ad2deec22b8d27575d21e2b786845.png)
unapply方法
·unapply方法用于对对象进行解构操作,与apply方法类似,该方法也会被自动调用
·可以认为unapply方法是apply方法的反向操作,apply方法接受构造参数变成对象,而unapply方法接受一个对象,从中提取值
![](https://i-blog.csdnimg.cn/blog_migrate/211a48f29e562ea9544c5dca0009ae6d.png)
继承
抽象类
如果一个类包含没有实现的成员,则必须使用abstract关键词进行修饰,定义为抽象类
![](https://i-blog.csdnimg.cn/blog_migrate/a78496dc674d70a34353ca7141a780b1.png)
关于上面的定义,说明几点:
(1)定义一个抽象类,需要使用关键字abstract
(2)定义一个抽象类的抽象方法,不需要关键字abstract,只要把方法体空着,不写方法体就可以
(3)抽象类中定义的字段,只要没有给出初始化值,就表示是一个抽象字段,但是,抽象字段必须要声明类型,否则编译会报错
扩展类
Scala只支持单一继承,而不支持多重继承。在类定义中使用extends关键字表示继承关系。定义子类时,需要注意:
重载父类的抽象成员(包括字段和方法)时,override关键字是可选的;而重载父类的非抽象成员时,override关键字是必选的
只能重载val类型的字段,而不能重载var类型的字段。因为var类型本身就是可变的,所以,可以直接修改它的值,无需重载
![](https://i-blog.csdnimg.cn/blog_migrate/2dd5e212531eb77a9cdfa1323eab3a7e.png)
Scala的类层次结构
![](https://i-blog.csdnimg.cn/blog_migrate/921d7c3d61790238b9364e7812449c6f.png)
Null是所有引用类型的子类,其唯一的实例为null,表示一个“空”对象,可以赋值给任何引用类型的变量,但不能赋值给值类型的变量
Nothing是所有其它类型的子类,包括Null。Nothing没有实例,主要用于异常处理函数的返回类型
Option类
Scala提供null是为了实现在JVM与其它Java库的兼容性,但是,除非明确需要与Java库进行交互,否则,Scala建议尽量避免使用这种可能带来bug的null,而改用Option类
Option是一个抽象类。有一个具体的子类Some和一个对象None,其中,前者表示有值的情形,后者表示没有值
当方法不确定是否有对象返回时,可以让返回值类型为Option[T],其中,T为类型参数。对于这类方法,如果确实有T类型的对象需要返回,会将该对象包装成一个Some对象并返回;如果没有值需要返回,将返回None
![](https://i-blog.csdnimg.cn/blog_migrate/a0201b24060d7ea5102dcbb9af10cd16.png)
特质
特质概述
Java中提供了接口,允许一个类实现任意数量的接口
Scala中没有接口的概念,而是提供了“特质(trait)”,它不仅实现了接口的功能,还具备了很多其他的特性
Scala的特质是代码重用的基本单元,可以同时拥有抽象方法和具体方法
Scala中,一个类只能继承自一个超类,却可以实现多个特质,从而重用特质中的方法和字段,实现了多重继承
特质的定义
使用关键字trait定义特质
![](https://i-blog.csdnimg.cn/blog_migrate/3e5731d944f9b86bb78e236e0c44c605.png)
特质既可以包含抽象成员,也可以包含非抽象成员。包含抽象成员时,不需要abstract关键字
特质可以使用extends继承其它的特质
把特质混入类中
可以使用extends或with关键字把特质混入类中
如果特质中包含抽象成员,则该类必须为这些抽象成员提供具体实现,除非该类被定义为抽象类
![](https://i-blog.csdnimg.cn/blog_migrate/71c0ad88a9843aa0ffa384b881867efa.png)
把上面定义的特质Flyable和类Bird封装到一个代码文件Bird.scala中:
![](https://i-blog.csdnimg.cn/blog_migrate/ea3ce5b904e543bb9337b3b67f9c5cfe.png)
在Scala REPL中执行如下代码并观察效果:
![](https://i-blog.csdnimg.cn/blog_migrate/bd92f750178e5ac652afdb5c2c11bd43.png)
如果要混入多个特质,可以连续使用多个with
![](https://i-blog.csdnimg.cn/blog_migrate/d85ccffb0c6dd14ca254d881e5aa448d.png)
可以在Scala REPL中执行如下代码查看执行效果:
![](https://i-blog.csdnimg.cn/blog_migrate/e02226dad073165911606fd984cca943.png)
模式匹配
match语句
最常见的模式匹配是match语句,match语句用在当需要从多个分支中进行选择的场景
![](https://i-blog.csdnimg.cn/blog_migrate/6dc7aec42913bb5bc002ea9199683043.png)
通配符_相当于Java中的default分支
match结构中不需要break语句来跳出判断,Scala从前往后匹配到一个分支后,会自动跳出判断
case后面的表达式可以是任何类型的常量,而不要求是整数类型
![](https://i-blog.csdnimg.cn/blog_migrate/97940cc510991ef64bd8dcf602f9e681.png)
除了匹配特定的常量,还能匹配某种类型的所有值
![](https://i-blog.csdnimg.cn/blog_migrate/0f6fb54fce3c80e35ec677c4f7e4e8ba.png)
可以在match表达式的case中使用守卫式(guard)添加一些过滤逻辑
![](https://i-blog.csdnimg.cn/blog_migrate/42e0f093d5540799039c86aa3035b58b.png)
case类
case类是一种特殊的类,它们经过优化以被用于模式匹配
当定义一个类时,如果在class关键字前加上case关键字,则该类称为case类
Scala为case类自动重载了许多实用的方法,包括toString、equals和hashcode方法
Scala为每一个case类自动生成一个伴生对象,其包括模板代码
- 1个apply方法,因此,实例化case类的时候无需使用new关键字-1个unapply方法,该方法包含一个类型为伴生类的参数,返回的结果是Option类型,对应的类型参数是N元组,N是伴生类中主构造器参数的个数。
Unapply方法用于对对象进行解构操作,在case类模式匹配中,该方法被自动调用,并将待匹配的对象作为参数传递给它
例如,假设有如下定义的一个case类:
![](https://i-blog.csdnimg.cn/blog_migrate/2ff302a79ce1ab28c18ffeee50f26464.png)
则编译器自动生成的伴生对象是:
![](https://i-blog.csdnimg.cn/blog_migrate/49e247445eabba2a10bebe1113912112.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fb3c1a3a865969c8b139afb8925fab99.png)
包
包的定义
为了解决程序中命名冲突问题,Scala也和Java一样采用包(package)来层次化、模块化地组织程序
![](https://i-blog.csdnimg.cn/blog_migrate/258ca122a17a63db17948115b2803112.png)
为了在任意位置访问MyClass类,需要使用autodepartment.MyClass
通过在关键字package后面加大括号,可以将程序的不同部分放在不同的包里。这样可以实现包的嵌套,相应的作用域也是嵌套的
![](https://i-blog.csdnimg.cn/blog_migrate/eba82a1ec79ba41e05e10765d9078856.png)
引用包成员
可以用import子句来引用包成员,这样可以简化包成员的访问方式
![](https://i-blog.csdnimg.cn/blog_migrate/b48652667eadcbd1d7c3f7dbe01b9e4b.png)
使用通配符下划线(_)引入类或对象的所有成员
![](https://i-blog.csdnimg.cn/blog_migrate/ae9db047ab8f4d91de9618c9a7f3cd9b.png)
Scala隐式地添加了一些引用到每个程序前面,相当于每个Scala程序都隐式地以如下代码开始:
![](https://i-blog.csdnimg.cn/blog_migrate/81b2ecdf1df2d8558ff98ccadbc12457.png)