9 类
类和对象是Java、C++等面向对象编程的基础概念。类是用来创建对象的蓝图。定义好类以后,就可以使用new关键字来创建对象。
scala 如果不写权限修饰符,默认是public。
一个类文件可以声明多个类;
定义语法:
//模板类
class 类名{
}
其中:
类中可以放属性、方法、函数;
类中属性的定义和变量的定义一样,用val 或 var 进行定义,默认权限是public;
类中方法用关键字def 定义;
object对象,也称单例对象,该对象编译后都是静态成员;
如果有同样一个类与该object名字一样,则称该object为该类的伴生对象,相对应,该类为object的伴生类;
类中的属性:
可以分为可变(var)和不可变(val)
没有setget方法但是可以增加toString方法
示例:
class Student{
// private String name
// val name:String = "zhangsan"
// var age:Int = 20
//
def sayHello() = println("hello",name)
var name:String = _
var age:Int = _
// val company = _
override def toString: String = "name = "+name +" age "+ age
}
object TestStudent{
def main(args: Array[String]): Unit = {
// val student = new Student
// student.age = 30
// student.name = "lisi"
// println(student.name)
// student.sayHello()
val student = new Student
student.name = "zhangsan"
student.age = 18
println(student)
}
10 构造器
用于对类进行属性的初始化,分为主构造器和辅助构造器;
10.1 主构造器
语法:
//参数可以有默认值
class 类名 (参数列表){
}
其中:
1)定义类的时候,就定义了构造器。即:是()内的内容。
2)主构造器会执行类中定义的所有语句。
package day02
// 主构造器参数如果是var、val修饰的,那就是public
// var:可变, val:不可变
class ConDemo1(var name:String, val age:Int) {
}
object ConDemo1Demo{
def main(args: Array[String]): Unit = {
// 通过主构造器创建对象
val demo = new ConDemo1("傻强", 10)
println(s"name:${demo.name}, age:${demo.age}")
demo.name = "奀妹"
// demo.age = 10
}
}
4)主构造器内的参数可以是默认值;当参数有默认值,且参数在后面时,创建对象时可以不用给参数赋值;
package day02
// 主构造器参数如果是var、val修饰的,那就是public
// var:可变, val:不可变
class ConDemo1(var name:String, val age:Int=10) {
}
object ConDemo1Demo{
def main(args: Array[String]): Unit = {
// 通过主构造器创建对象
// val demo = new ConDemo1("傻强")
val demo = new ConDemo1("傻强", 11)
println(s"name:${demo.name}, age:${demo.age}")
demo.name = "奀妹"
// demo.age = 10
}
}
5)主构造器内的参数可以用var、val修饰,也可以不加 var、val 修饰
a)var 修饰的参数,可以在其他类中访问和修改;
b)val 修饰的参数,只可以在其他类中访问,但不能修改;
c)不加 var、val 修饰的参数,就相当于参数,不可以在其他类中访问和修改;
// 如果主构造器的参数没有var、val
class ConDemo2(var name:String, val age:Int=10, addr:String = "四川") {
def printInfo = println(this.addr)
}
object ConDemo2{
def main(args: Array[String]): Unit = {
val demo = new ConDemo2("傻强")
println(s"name:${demo.name}, age:${demo.age}")
// demo.addr
}
}
object ConDemo2Demo{
def main(args: Array[String]): Unit = {
// 通过主构造器创建对象
val demo = new ConDemo2("傻强")
println(s"name:${demo.name}, age:${demo.age}")
// demo.addr
demo.printInfo
}
}
10.2 辅助构造器
赋值构造器的名称是 this;
辅助构造器可以理解成java中的构造器重载,且辅助构造器的第一行必须要先调用主构造器(这点和python 的基础调用父类比较相似);
辅助构造器可以没有,如果有要在类中进行定义,辅助构造器可以声明主构造器没有的参数,可以通过辅助构造给属性赋值;
辅助构造器的参数不能用val或者var修饰,因为其参数都是参考主构造器或者类中的;
@BeanProperty自动生成setget方法
object TestStudent {
def main(args: Array[String]): Unit = {
// val student = new Student(1,"zhangsan","haima")
// student.age = 20
//// student.name
//// student.name = "lisi"
//// student.id = 2
// println(student)
// student.sayHello
val s1 = new Student(1, "zhangsan", company = "hainiu", age = 20)
val s2 = new Student(1, "zhangsan", company = "hainiu", age = 20, "beijing")
println(s1)
println(s2)
}
}
//set get toString construction
class Student( @BeanProperty var id: Int,@BeanProperty var name: String,@BeanProperty val company: String = "hainiu") {
@BeanProperty var age: Int = _
@BeanProperty var address: String = _
// def this(){
// this(1,"xx","hainiu")
// }
def this(id: Int, name: String, company: String, age: Int) {
this(id, name, company)
this.age = age
}
def this(id: Int, name: String, company: String, age: Int, address: String) {
this(id, name, company, age)
this.address = address
}
// public Student(){}
override def toString: String = s"id = ${id} name = ${name} age = ${age} company = ${company} age = ${age} address = ${address}"
def sayHello = println(s"hello ${name}")
}
10.3 总结
主构造器
1)主构造器的参数列表要放到类名的后面,和类名放在一起,val修饰的构造参数具有不可变性,var修饰的构造参数具有可变性;
2)如果参数没有用val或var修饰,那它不可被外面直接访问(可通过相应的get方法访问),只能在本类中使用;
辅助构造器
1)辅助构造器可以没有;如果有,要在类中进行定义。
2)辅助构造器可以声明主构造器没有的参数,如果声明了其它参数那这个参数需要在类中进行定义,否则提示无法找到这个参数;
3)辅助构造器的参数不能用val或者var修饰,因为其参数都是参考主构造器或者类中的;
4)辅助构造器也可以理解成java中的构造器重载,且辅助构造器的第一行必须要先调用主构造器(这点和python的子类调用父类比较相似,而JAVA可以选择是否调用默认构造器)
idea使用:
如果按ctrl+p,提示有两行或多行的时候那就说明有辅助构造器;
快捷键修改
settings → Keymap,在搜索框输入 “parameter info”, 然后双击搜索结果“parameter info”,选择添加快捷键,再点applay → ok。
11 单例对象
在scala中没有像java一样的static关键字;
在scala中是没有静态方法和静态字段的,但是可以使用object关键字加类名的语法结构实现同样的功能;
在scala中用object修饰的为单例对象,单例对象中主要存放常量和工具方法;
单例对象使用
object Student{
val name = "hainiu"
def sayHello(name:String) = println(s"hello ${name}")
}
//scala static
//单例的静态的替换
object TestT{
println("init")
}
object Test{
def main(args: Array[String]): Unit = {
// new TestT()
val t1 = TestT
val t2 = TestT
val t3 = TestT
println(t1)
println(t2)
println(t3)
}
}
单例对象的编译
上面代码通过反编译发现:
常量的存储
首先引入依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
实验室中初始化mysql组件
配置连接信息
object Constance {
val user_name = "root"
val password = "hainiu"
val url = "jdbc:mysql://11.147.251.92:3306/hainiu"
}
连接测试代码如下:
object TestConnection {
def main(args: Array[String]): Unit = {
val con = DriverManager.getConnection(Constance.url,Constance.user_name,Constance.password)
val prp = con.prepareStatement("create table student(id int,name varchar(50),age int,primary key(id))")
prp.execute()
}
}
连接池案例
定义单例对象,实现连接池的操作, 提供获取可用连接个数、获取连接、释放连接
object MyConnectionUtils {
val buffer = ListBuffer[Connection]()
for(i<-1 to 3){
val con = DriverManager.getConnection(Constance.url, Constance.user_name, Constance.password)
buffer.append(con)
}
def getCon = {
if(buffer.size>0)
buffer.remove(0)
else{
println("connection pool has not connection....")
null
}
}
def retCon(con:Connection) = {
if(con == null){
println("return connection is invalid ....")
}else{
this.buffer.append(con)
}
}
}
测试连接池
object TestConnection {
def main(args: Array[String]): Unit = {
val con1 = MyConnectionUtils.getCon
val con2 = MyConnectionUtils.getCon
val con3 = MyConnectionUtils.getCon
val con4 = MyConnectionUtils.getCon
println(con1)
println(con2)
println(con3)
println(con4)
val prp = con1.prepareStatement("create table teacher(id int,name varchar(50),age int,primary key(id))")
prp.execute()
MyConnectionUtils.retCon(con1)
MyConnectionUtils.retCon(con2)
MyConnectionUtils.retCon(con3)
MyConnectionUtils.retCon(con4)
}
}
12 伴生对象
在Scala中,单例对象分为两种,一种是并未自动关联到特定类上的单例对象,称为独立对象 ;
另一种是关联到一个类上的单例对象,该单例对象与该类共有相同名字,则这种单例对象称为伴生对象,对应类称为伴生类。
一个单例对象未必是一个伴生对象,但是一个伴生对象一定是一个单例对象;
每个类都可以有伴生对象,伴生类与伴生对象必须写在同一个文件中;
利用类和伴生对象,解析ip对应的主机名
object Teacher{
def main(args: Array[String]): Unit = {
val teacher = new Teacher("uncle wang", 18)
println(teacher.name)
}
def spark = println("spark chinese!!!")
}
object TestTeacher{
def main(args: Array[String]): Unit = {
val teacher = new Teacher("uncle wang", 18)
// teacher.name
Teacher.spark
}
}
class IpUtils(ip:String){
def getHost = {
map.getOrElse(ip, "unknow")
}
}
object IpUtils{
val map = mutable.Map[String,String]()
map.put("192.168.88.199","nn1.hadoop")
map.put("192.168.88.189","nn2.hadoop")
map.put("192.168.88.179","s1.hadoop")
}
object TestIpUtils{
def main(args: Array[String]): Unit = {
val utils = new IpUtils("192.168.881.189")
println(utils.getHost)
}
}
13 private关键字总结
修饰class
1、在class前面使用private可以被相同包(包含递归子包)访问(能引入类);
2、在class前面使用private[包名]代表是包的访问权限,只能指定的包名和子包(包含递归子包)下才能访问;
private修饰 主构造器、主构造器参数、辅助构造器、属性、方法, 当前类和伴生对象可以访问,其他对象不能访问;
private[this]修饰 主构造器、主构造器参数、辅助构造器、属性、方法, 只有当前类可以访问;
private[包名] 修饰 主构造器、主构造器参数、辅助构造器、属性、方法, 指定包名及子包可访问。
示例:
在class前面使用private可以被相同包(包含递归子包)访问(能引入类);
子包可引入
其他包不能引入
在class前面使用private[包名]代表是包的访问权限,只能指定的包名和子包(包含递归子包)下才能访问;
package day03
// private[this] 修饰的主构造器,伴生对象和其他对象都不可访问
class PrivateDemo2 private[this] (val name:String) {
var age:Int = _
// private[包名] 修饰的辅助构造器,同包名或递归子包都可访问
private[day03] def this(name:String, age:Int ) = {
this(name)
this.age = age
}
}
object PrivateDemo2{
def main(args: Array[String]): Unit = {
// val demo = new PrivateDemo2("hainiu")
val demo = new PrivateDemo2("hainiu", 10)
}
}
object privateDemo2Other{
def main(args: Array[String]): Unit = {
// val demo = new PrivateDemo2("hainiu")
val demo = new PrivateDemo2("hainiu", 10)
}
}
14 apply和unapply方法
apply、unapply方法都被隐式的调用
apply方法:
1)在一个类的伴生对象中定义apply方法,在生成这个类的对象时,就省去了new关键字。
2)apply方法的参数列表不需要和构造函数的参数列表统一,也就是说apply 方法支持重载。
3)apply 方法可以通过主构造器和辅助构造器new对象;
4)apply方法 定义在object 里,是创建对象;如果定义在class 里,是获取对象的数据;
apply方法定义在object对象
class Cat private (val name:String,val age:Int){
def apply(i:Int)= {
if(i ==0)
this.name
else if(i == 1)
this.age
else
"invalid index"
}
}
object Cat{
var cat:Cat = _
def apply(name: String, age: Int): Cat = {
if(cat == null)
cat = new Cat(name,age)
cat
}
def unapply(arg: Cat): Option[(String, Int)] = Some(arg.name, arg.age)
}
object TestCat{
def main(args: Array[String]): Unit = {
// val cat = new Cat("ergou", 2)
// val cat1 = Cat.apply("ergou", 2)
// val cat2 = Cat("ergou", 2)
// println(cat)
// println(cat1)
// println(cat2)
}
apply方法定义在class上
class Cat private (val name:String,val age:Int){
def apply(i:Int)= {
if(i ==0)
this.name
else if(i == 1)
this.age
else
"invalid index"
}
}
object Cat{
var cat:Cat = _
def apply(name: String, age: Int): Cat = {
if(cat == null)
cat = new Cat(name,age)
cat
}
def unapply(arg: Cat): Option[(String, Int)] = Some(arg.name, arg.age)
}
object TestCat{
def main(args: Array[String]): Unit = {
val Array(a,b) = Array(1,2)
println(a,b)
}
}
unapply方法:
1)可以认为unapply方法是apply方法的反向操作,apply方法接受构造参数变成对象,而unapply方法接受一个对象,从中提取值。
2)unapply方法常被称为提取方法,可以用unapply方法提取相同操作的对象,unapply方法会返回一个Option,其内部生成一个Some对象,这个Some是做相似对象的封装。
3)unapply 不支持重载。
4)unapply常被用于模糊匹配。
Option Option中的泛型个数影响unapply方法返回some参数的个数和模式匹配中的case类型参数的个数。 Option 子类: None:无数据,scala 用None 来表达 无数据, 相当于java 的 null Some:有数据。 用的地方: 1)模式匹配 2)集合
class Cat private (val name:String,val age:Int){
def apply(i:Int)= {
if(i ==0)
this.name
else if(i == 1)
this.age
else
"invalid index"
}
}
object Cat{
var cat:Cat = _
def apply(name: String, age: Int): Cat = {
if(cat == null)
cat = new Cat(name,age)
cat
}
def unapply(arg: Cat): Option[(String, Int)] = Some(arg.name, arg.age)
}
object TestCat{
def main(args: Array[String]): Unit = {
val Cat(name,age) = Cat("ergou",2)
println(name,age)
}
}
总结:
apply 方法定义 object 中,用于创建对象;
apply方法定义 class 中, 用于提取数据;
unapply 方法 定义在 object 对象中, 用于提取对象数据;
15 样例类和样例对象
样例类
在class 前加上 case 关键字,这样的类称为样例类。默认实现了Serializable接口,可以封装数据。
scala 为每一个样例类自动生成一个伴生对象,在该伴生对象中自动生成的模板代码包括:
1)一个apply 方法,实例化该类的时候无需使用new 关键字;
2)一个unapply 方法,该方法包含一个类型为伴生类的参数,返回结果是Option 类型;
对应的类型参数是N元组,N是伴生类中主构造参数的个数;
unapply 方法用于对对象的解构操作,在case 类模式匹配中,该方法被自动调用,并将待匹配的对象作为参数传递给它;
如:
// 定义case类
case class Person(name:String, age:Int){}
//编译器自动生成的伴生对象是
object Person{
// apply 方法
def apply(name:String,age:Int) = new Person(name,age)
// unapply 方法
def unapply(p:Person):Option((String,Int)) = Some((p.name,p.age))
}
样例对象
样例对象也是一个对象只是在实现的时候在object前面加上了case用于支持模式匹配,默认实现了Serializable接口,不可以封装数据。
case object 对象名
示例:
package day03
import scala.util.Random
// 定义无构造参数样例类
case class CaseClass() {
def say = println("say hello")
}
// 定义有构造参数的样例类
case class CaseClass2(val name:String){
def say = println(s"say ${name}")
}
// 定义样例对象
case object CaseObject{
def say = println("say object")
}
object CaseTest{
def main(args: Array[String]): Unit = {
val arr = Array(CaseClass(), CaseClass2("hainiu"), CaseObject)
val data = arr(Random.nextInt(arr.size))
println(data)
data match {
case x:CaseClass => x.say
// case x:CaseClass2 => x.say
case CaseClass2(x) => println(x)
case x:CaseObject.type => x.say
}
}
}
反编译后查看:
代码
scala> class Student(val name:String,val age:Int)
defined class Student
scala> val s1 = new Student("zhangsan",20)
s1: Student = Student@4a23350
scala> s1
res0: Student = Student@4a23350
scala> s1.hashCode
res1: Int = 77738832
scala> val s2 = new Student("zhangsan",20)
s2: Student = Student@2921199d
scala> s2
res2: Student = Student@2921199d
scala> s2.hashCode
res3: Int = 690035101
scala> s2
res4: Student = Student@2921199d
scala> val Student(name,age) = s2
<console>:12: error: not found: value Student
val Student(name,age) = s2
^
scala> case class Student
<console>:1: error: case classes must have a parameter list; try 'case class Student()' or 'case object Student'
case class Student
^
scala> case class Student(val name:String,val age:Int)
defined class Student
scala> val s1 = Student("zhangsan",20)
s1: Student = Student(zhangsan,20)
scala> s1
res5: Student = Student(zhangsan,20)
scala> val s2 = Student("zhangsan",20)
s2: Student = Student(zhangsan,20)
scala> s1 == s2
res6: Boolean = true
scala> s1.hashCode
res7: Int = -2049191484
scala> s2.hashCode
res8: Int = -2049191484
scala> val Student(name,age) = s1
name: String = zhangsan
age: Int = 20
16 抽象类(abstract class)与 特质(trait)
抽象类:
抽象类与Java相似,只是Java中没有属性的抽象,scala可以有属性的抽象;
特质:
可以把特质理解成Java中升级版的接口
在Java中接口不能声明没有值的属性和有实现的方法,而Scala可以声明没有值的属性和有实现的方法;
重写:
重写与Java相似,只是Java中没有重写属性的概念,而 scala 可以重写属性;
特质和抽象类的使用区别:
只能继承一个抽象类,但可以实现多个特质。这点和Java一样;
下面分别详细说明
16.1 抽象类
1)重写父类的非抽象成员(包括字段和方法)时,必须加上override 关键字;
package day03
abstract class Car {
// 定义普通属性
val name:String = "车"
// 定义抽象属性(属性不赋值)
val brand:String
// 定义普通方法
def description = {
println("这是抽象类里面的普通方法")
}
// 定义抽象方法(方法没有方法体)
def action():Unit
}
// 定义子类继承父类
class BYDCar extends Car{
// 子类重写父类的抽象成员,可加可不加 override
val brand: String = "比亚迪"
def action(): Unit = {
println("研发的刀片电池,使用更安全")
}
// 子类重写父类的非抽象成员,必须加override
override val name: String = "电车"
override def description: Unit = {
super.description
println(s"${brand} ${name}")
action()
}
}
object CarDemo{
def main(args: Array[String]): Unit = {
val car = new BYDCar
car.description
}
}
2)如果父类有构造器,则子类主构造器必须调用父类的主构造器或辅助构造器。
子类的辅助构造器,不能调用父类的构造器。
package day03
abstract class Car(val color:String) {
var price:Double = _
def this(color:String, price:Double) = {
this(color)
this.price = price
}
// 定义普通属性
val name:String = "车"
// 定义抽象属性(属性不赋值)
val brand:String
// 定义普通方法
def description = {
println("这是抽象类里面的普通方法")
}
// 定义抽象方法(方法没有方法体)
def action():Unit
}
// 定义子类继承父类
// 子类主构造器继承父类的主构造器
class BYDCar(color:String, types:String) extends Car(color:String){
// 子类重写父类的抽象成员,可加可不加 override
val brand: String = "比亚迪"
def action(): Unit = {
println("研发的刀片电池,使用更安全")
}
// 子类重写父类的非抽象成员,必须加override
override val name: String = "电车"
override def description: Unit = {
super.description
println(s"${brand} ${color} ${types} ${name}")
action()
}
}
// 继承父类的辅助构造器
class WULINGCar(color:String,price:Double, types:String) extends Car(color:String, price:Double){
// 子类重写父类的抽象成员,可加可不加 override
val brand: String = "五菱"
def action(): Unit = {
println("大众的选择,销量杠杠滴")
}
// 子类重写父类的非抽象成员,必须加override
override val name: String = "电车"
override def description: Unit = {
super.description
println(s"${brand} ${color} ${types} ${name}")
println(s"亲民价:${price}")
action()
}
}
object CarDemo{
def main(args: Array[String]): Unit = {
val car = new BYDCar("炫彩蓝", "秦DMI油电混")
car.description
println("-----------------")
val car2 = new WULINGCar("各种颜色", 28800,"宏光Mini")
car2.description
}
}
16.2 特质
定义特质需要用 trait 关键字;
特质可以包含抽象成员和非抽象成员,这与scala 的抽象类类似,包含抽象成员时,不需要abstract 关键字;
在Scala中,无论继承类还是继承Trait都是用extends关键字;
在重写特质的方法时,不需要给出override 关键字;
package day03
trait Fly {
// 定义普通属性
val name:String = "飞"
// 定义抽象属性
val maxFlyHigh:Int
// 定义普通方法
def description = {
println("这是特质里面的普通方法")
}
// 定义抽象方法
def action():Unit
}
class Bird extends Fly{
// 重写抽象成员
val maxFlyHigh: Int = 1000
def action(): Unit = {
println("鸟用翅膀飞")
}
// 重写非抽象成员
override val name: String = "火烈鸟"
override def description: Unit = {
super.description
println(s"${name}")
action()
println(s"最大飞行高度:${maxFlyHigh}")
}
}
object TraitTest{
def main(args: Array[String]): Unit = {
val bird = new Bird
bird.description
}
}
当不继承类直接实现一个特质,可以不用with直接用extends,实现多个特质的时候可以用多个with,但是必须先extends第一个
如: class T1 extends T2 with T3 with T4
package day03
trait Fly {
// 定义普通属性
val name:String = "飞"
// 定义抽象属性
val maxFlyHigh:Int
// 定义普通方法
def description = {
println("这是特质里面的普通方法")
}
// 定义抽象方法
def action():Unit
}
trait Run{
def run():Unit
}
class Bird extends Fly{
// 重写抽象成员
val maxFlyHigh: Int = 1000
def action(): Unit = {
println("鸟用翅膀飞")
}
// 重写非抽象成员
override val name: String = "火烈鸟"
override def description: Unit = {
super.description
println(s"${name}")
action()
println(s"最大飞行高度:${maxFlyHigh}")
}
}
// 实现多个特质用with
class AirPlane extends Fly with Run{
override val maxFlyHigh: Int = 10000
override def action(): Unit = {
println("飞机用翅膀飞")
}
override def run(): Unit = {
println("飞机用轱辘跑")
}
override def description: Unit = {
super.description
action()
run()
println(s"最大飞行高度:${maxFlyHigh}")
}
}
object TraitTest{
def main(args: Array[String]): Unit = {
val bird = new Bird
bird.description
println("------------------")
val plane = new AirPlane
plane.description
}
}
特质通过字段的初始化和其他特质体中的语句构成来实现构造的逻辑。
但特质不能 new 实例。
trait T1{
// 特质构造执行
val a:String = "aaa"
println(a)
def t1():Unit
}
trait T2{
// 特质构造执行
val b:String = "bbb"
println(b)
def t2():Unit
}
class T1Sub extends T1 with T2{
//本类构造执行
val c:String = "ccc"
println(c)
def t1():Unit={
println("do t1()")
}
def t2():Unit={
println("do t2()")
}
}
object T1SubTest{
def main(args: Array[String]): Unit = {
// 调用 new T1Sub 时,先执行T1的构造,再执行T2的构造,然后执行本类构造
val t = new T1Sub
t.t1()
t.t2()
}
}
//-----------运行结果-----------------
aaa
bbb
ccc
do t1()
do t2()
动态混入特质
trait Penhuo{
def fire = println("fire in the hole!!!")
}
class Child
object TestChild{
def main(args: Array[String]): Unit = {
val c = new Child with Penhuo
c.fire
}
}
什么时候使用特质和抽象类?
使用角度:
主次关系用抽象类,额外功能用特质,比如蜘蛛侠。
语法角度:
1)优先使用特质。一个类扩展多个特质是很方便的,但却只能扩展一个抽象类。
2)如果你需要构造函数参数,使用抽象类。因为抽象类可以定义带参数的构造函数,而特质不行。
16.3 对象和实例的类型以及type关键字
object Dog
class Dog
object TestDog{
def main(args: Array[String]): Unit = {
// val d1: Dog.type = Dog
// val d2: Dog = new Dog
// type???
type xx = (Int,String,Int,String,String)
val tp:xx = (1,"zhangsan",20,"beijing","male")
type myList = scala.collection.immutable.List[Int]
val list:myList = List[Int](1,2,3,4,5,6)
}
}
17 模式匹配
17.1 match 语句
match 语句用在当需要从多个分支中进行选择的场景,类似于java 中的switch 语句。
语法:
变量 match{
case "值" => 语句块1 // 语句块后不用加break
case "值2" => 语句块2
case _ => 语句块N // 类似于java的default
}
其中:
1)case 后面的表达式可以是任何类型的常量,如字段串、类、元组、集合等;
2)与java的switch不同的是,match 结构中不需要break 语句来跳出判断;
3)最后一个case语句用了通配符“_”,相当于java的default;
4)如果匹配不到,就会报错;
17.2 字符串匹配
def main(args: Array[String]): Unit = {
// while(true){
// val number = StdIn.readInt()
// if (number == 1)
// println("start")
// else if (number == 2)
// println("running")
// else if (number == 3)
// println("stop")
// }
while(true){
val number = StdIn.readInt()
number match {
case 1 => println("start")
case 2 => println("running")
case 3 => println("stop")
case _ => println("invalid number")
}
}
返回值和守卫
// val age = 20
// val res = age match {
// case 18 => 10
// case 20 => 200
// }
// println(res)
val name = "zhangsan"
val age = 20
val res: AnyVal = name match {
case "zhangsan" if (age > 18) => 10000
case "lisi" => 8000
case "wangwu" => 8000
case _ => println("think think ..")
}
println(res)
// name match {
// case "zhangsan" if(age>18) => println("u r in!!!")
// case "lisi" => println("not in")
// case "wangwu" =>println("not in")
// case _ => println("think think ..")
// }
17.3 类型匹配
match除了匹配特定的常量,还能匹配某种类型的所有值;
在scala 中倾向于用这样的模式匹配,而不是isInstanceOf 操作符;
object TestMatch{
def main(args:Array[String]):Unit = {
val value:Any = 20
// if(value.isInstanceOf[Int]){
// println("Int")
// }
value match {
case x:Bird => println(x,"bird")
case x:Int if(x>18) => println(x,"Int")
case x:Int if(x>18) => println(x,"Int1")
case x:String => println(x,"String")
case x:Boolean => println(x,"Boolean")
case _ => println("unknow type!!")
}
}
}
class Bird
17.4 数组、元组、集合匹配、映射
元组匹配时case后面的值的个数应与被匹配的元组中数据的个数相同,否则报错。
当有多个条件能匹配到时以最先匹配到的条件为准
// val arr = Array(1,2,3,4)
// arr match {
//// case x:Array[Int] => println(1)
//// case Array(1,2,3,4) => println(2)
//// case Array(a,b,c,d) => println(a,b,c,d)
//// case Array(1,2,a,b) => println(a,b)
// case Array(a,b,_,_) => println(a,b)
// case _ => println("no match!!")
// }
// val tp = (1,"zhangsan",20)
// tp match {
// case (1,"zhangsan",20) => println("1")
// case x:(Int,String,Int) => println("2")
// case (a,b,c) => println(a,b,c)
// case (a,b,_) => println(a,b)
// case Tuple3(a,b,c) => println(a,b,c)
// case _ => println("unknow type")
// }
// val list = List(1,2,3,4)
// list match {
// case List(1,2,3,4) => println("1")
// case x:List[Int] => println(x)
// case List(a,b,c,d) => println(a,b,c,d)
// case List(a,b,_,_) => println(a,b)
// case a::b::c::d => println(a,b,c,d)
// case a::b::c::d::Nil => println(a,b,c,d)
// }
// val map = Map("zhangsan"->20,"lisi"->30)
// map.get("zhangsan1") match {
// case Some(v) => println(v)
// case None => println("no value")
// }
17.5 偏函数匹配
偏函数的定义
scala> def add(x:Int,y:Int) = x+y
add: (x: Int, y: Int)Int
scala> add(10,20)
res0: Int = 30
scala> def doubleValue:PartialFunction[Int,Int]={
| case x:Int => x*2
| }
doubleValue: PartialFunction[Int,Int]
scala> doubleValue(10)
res1: Int = 20
scala> def add:PartialFunction[(Int,Int),Int]={
| case x => x._2+x._2
| }
add: PartialFunction[(Int, Int),Int]
scala> add(1,2)
res2: Int = 4
scala> //定义一个偏函数 输入一个值,如果是偶数就返回,如果是奇数就乘2
scala> def double:PartialFunction[Int,Int]={
| case x if(x%2==0) => x
| case x => x*2
| }
double: PartialFunction[Int,Int]
scala> double(1)
res3: Int = 2
scala> double(2)
res4: Int = 2
1)使用 case 语句构造匿名函数与偏函数。
scala> arr
res5: Array[Int] = Array(1, 2, 3, 4, 5, 6)
scala> arr.map(t=> t*2)
res6: Array[Int] = Array(2, 4, 6, 8, 10, 12)
scala> arr.map({
| case x => x*2
| })
res7: Array[Int] = Array(2, 4, 6, 8, 10, 12)
scala> arr.map{
| case x => x*2
| }
res8: Array[Int] = Array(2, 4, 6, 8, 10, 12)
scala> arr.collect{
| case x => x*2
| }
res9: Array[Int] = Array(2, 4, 6, 8, 10, 12)
scala> val arr = Array(1,2,3,4,5,"6")
arr: Array[Any] = Array(1, 2, 3, 4, 5, 6)
scala> arr.map{
| case x:Int => x*2
| }
scala.MatchError: 6 (of class java.lang.String)
at .$anonfun$res10$1(<console>:13)
at .$anonfun$res10$1$adapted(<console>:13)
at scala.collection.TraversableLike.$anonfun$map$1(TraversableLike.scala:286)
at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:36)
at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:33)
at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:198)
at scala.collection.TraversableLike.map(TraversableLike.scala:286)
at scala.collection.TraversableLike.map$(TraversableLike.scala:279)
at scala.collection.mutable.ArrayOps$ofRef.map(ArrayOps.scala:198)
... 30 elided
scala> arr.collect{
| case x:Int => x*2
| }
res11: Array[Int] = Array(2, 4, 6, 8, 10)
总结:
当编译器把case语句翻译成函数时,如果匹配不上会报错;
当编译器把case语句翻译成偏函数时,匹配不上的放弃并过滤掉;
2)偏函数
在Scala中,所有偏函数的类型皆被定义为 PartialFunction[-A, +B] 类型,PartialFunction[-A, +B] 又派生自 Function1 。
PartialFunction[-A, +B] ,其中 A 是方法参数类型,B是方法返回值类型。
PartialFunction(偏函数)内部常与 case 语句搭配,进行模式匹配,函数体里的模式匹配没有match关键字;
当把偏函数作为函数使用时,如果匹配不上会报错;
当把偏函数作为偏函数时,匹配不上的放弃并过滤掉;
package day03
object MatchDemo3 {
// 定义普通方法
def m1(data:Any):Int = {
data match {
case x:Int => x * 10
// case x:String => x.toInt * 10
// case _ => 0
}
}
// 定义偏函数
def func:PartialFunction[Any, Int] = {
case x:Int => x * 10
}
def main(args: Array[String]): Unit = {
println(m1(10))
//println(m1("10"))
println(func(10))
val list = List(1,2,3, "aa", "bb")
// collect接收偏函数,func定义时只要Int
println(list.collect(func))
println(list.map(m1))
}
}