Scala——(继承,特质,模式匹配和样例类)
Posted 想做CTO的任同学...
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala——(继承,特质,模式匹配和样例类)相关的知识,希望对你有一定的参考价值。
文章目录
继承的概念
-
Scala中继承类的方式和Java一样,也是使用extends关键字,
-
和Java一样,可在定义中给出子类需要而父类没有的字段和方法,或者重写父类的方法。
//Person类 class Person(name:String,age:Int) //Student继承Person类 class StudentOne(name:String,age:Int,var studentNo:String) extends Person(name,age) def sayHi(): Unit = println(s"大家好,我是$name,$age 岁,学号$studentNo") object Demo def main(args: Array[String]): Unit = val StudentOne=new StudentOne("john",18,"1024") StudentOne.sayHi()
构造器执行顺序
-
Scala在继承的时候构造器的执行顺序:首先执行父类的主构造器,其次执行子类自身的主构造器。
-
类有一个主构造器和任意数量的辅助构造器,而每个辅助构造器都必须以对先前定义的辅助构造器或主构造器的调用开始。
-
子类的辅助构造器最终都会调用主构造器。只有主构造器可以调用父类的构造器
//Person类 class Person(name:String,age:Int) println("这是父类Person") //Student继承Person类 class StudentOne(name:String,age:Int,var studentNo:String) extends Person(name,age) println("子类StudentOne") def sayHi(): Unit = println(s"大家好,我是$name,$age 岁,学号$studentNo") object Demo def main(args: Array[String]): Unit = //下面的语句执行时会打印下列内容: //这是父类Person //这是子类Student //也就是说,构造Student对象之前,首先会调用Person的主构造器 val StudentOne=new StudentOne("john",18,"1024") StudentOne.sayHi()
override方法重写
-
方法重写指的是当子类继承父类的时候,从父类继承过来的方法不能满足子类的需要,子类希望有自己的实现,这时需要对父类的方法进行重写,方法重写是实现多态的关键
-
Scala中的方法重写同Java一样,也是利用override关键字标识重写父类的方法。
class Programmer(name:String,age:Int) def coding():Unit=println("我在写代码...") //ScalaProgrammer继承Programmer类 class ScalaProgrammer(name:String,age:Int,workNo:String) extends Programmer(name,age) override def coding():Unit= //调用父类的方法 super.coding() //增加了自己的实现 println("我在写Scala代码...") object ProgrammerScala def main(args: Array[String]): Unit = val scalaProgrammer=new ScalaProgrammer("张三",30,"1001") scalaProgrammer.coding()
-
如果父类是抽象类,则override关键字可以不加。如果继承的父类是抽象类(假设抽象类为AbstractClass,子类为SubClass),在SubClass类中,AbstractClass对应的抽象方法如果没有实现的话,那SubClass也必须定义为抽象类,否则的话必须要有方法的实现。
//抽象的Person类 abstract class Person(name:String,age:Int) def walk():Unit //Student继承抽象Person类 class Student(name:String,age:Int,var studentNo:String) extends Person(name,age) //重写抽象类中的walk方法,可以不加override关键字 def walk():Unit= println("walk like a elegant swan") object Demo def main(args: Array[String]): Unit = val stu=new Student("john",18,"1024") stu.walk()
类型检查与转换
-
要测试某个对象是否属于某个给定的类,可以用isInstanceOf方法。如果测试成功,可以用asInstanceOf方法进行类型转换。
if(p.isInstanceOf[Employee]) //s的类型转换为Employee val s = p.asInstanceOf[Employee]
-
如果p指向的是Employee类及其子类的对象,则p.isInstanceOf[Employee]将会成功。
-
如果p指向的是Employee类及其子类的对象,则p.isInstanceOf[Employee]将会成功。
-
如果p指向的是Employee类及其子类的对象,则p.isInstanceOf[Employee]将会成功。
-
如果想要测试p指向的是一个Employee对象但又不是其子类,可以用:
if(p.getClass == classOf[Employee])
-
classOf方法定义在scala.Preder对象中,因此会被自动引入。不过,与类型检查和转换相比,模式匹配通常是更好的选择。
p match //将s作为Employee处理 case s: Employee => ... //p不是Employee的情况 case _ => ....
特质
作为接口使用的特质
-
Scala中的trait特质是一种特殊的概念。首先可以将trait作为接口来使用,此时的trait就与Java中的接口非常类似。
-
在trait中可以定义抽象方法,与抽象类中的抽象方法一样,只要不给出方法的具体实现即可。类可以使用extends关键字继承trait。
-
在Scala中没有implement的概念,无论继承类还是trait特质,统一都是extends。
-
类继承trait特质后,必须实现其中的抽象方法,实现时可以省略override关键字。
-
Scala不支持对类进行多继承,但是支持多重继承trait特质,使用with关键字即可。
//定义一个trai特质 trait HelloTrait def sayHello //定义一个trai特质 trait MakeFriendTrait def makeFriend //继承多个trait,第一个trait使用extends关键字,其它trait使用with关键字 class Person(name: String) extends HelloTrait with MakeFriendsTrait with Serializable override def sayHello() = println("Hello, My name is " + name) //override关键字也可以省略 def makeFriend() = println("Hello," + name)
带有具体实现的特质
-
具体方法:Scala中的trait特质不仅仅可以定义抽象方法,还可以定义具体实现的方法,这时的trait更像是包含了通用工具方法的类。比如,trait中可以包含一些很多类都通用的功能方法,比如打印日志等等,Spark中就使用了trait来定义通用的日志打印方法。
-
具体字段:Scala trait特质中的字段可以是抽象的,也可以是具体的。
package com.scala.code trait People //定义抽象字段 val name: String //定义了age字段 val age = 30 def eat(message: String): Unit = println(message) trait Worker //这个trait也定义了age字段 val age = 25 def work: Unit = println("Working......") // Student类继承了Worker、Person这两个特质,需要使用extends、with这两个关键字 class Student extends Worker with People //重写抽象字段,override可以省略 override val name: String = "张三" //继承的两个trait中都有age字段,此时需要重写age字段,override不能省略 override val age = 20 object TraitDemoTwo def main(args: Array[String]): Unit = val stu = new Student stu.eat("吃饭") stu.work println(s"Name is $stu.name, Age is $stu.age")
-
特质Person和Worker中都有age字段,当Student继承这两个特质时,需要重写age字段,并且要用override关键字,否则就会报错。
特质构造顺序
-
在Scala中,trait特质也是有构造器的,也就是trait中的不包含在任何方法中的代码。
-
构造器以如下顺序执行:
- 执行父类的构造器;
- 执行trait的构造器,多个trait从左到右依次执行;
- 构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;
- 所有trait构造完毕之后,子类的构造器才执行
class Person2 println("Person's constructor!") trait Logger println("Logger's constructor!") trait MyLogger extends Logger println("MyLogger's constructor!") trait TimeLogger extends Logger println("TimeLogger's constructor!") //类既继承了类又继承了特质,要先写父类 class Student2 extends Person2 with MyLogger with TimeLogger println("Student's constructor!")
特质继承类
-
在Scala中,trait特质也可以继承class类,此时这个class类就会成为所有继承此trait的类的父类。
class MyUtil def printMessage(msg: String) = println(msg) // 特质Log继承MyUtil类 trait Log extends MyUtil def log(msg: String) = printMessage(msg) // Person3类继承Log特质,Log特质继承MyUtil类,所以MyUtil类成为Person3的父类 class Person3(name: String) extends Logger def sayHello log("Hello, " + name) printMessage("Hi, " + name)
Ordered和Ordering
-
在Java中对象的比较有两个接口,分别是Comparable和Comparator。它们之间的区别在于:
- 实现Comparable接口的类,重写compareTo()方法后,其对象自身就具有了可比较性;
- 实现Comparator接口的类,重写了compare()方法后,则提供一个第三方比较器,用于比较两个对象。
-
在Scala中也引入了以上两种比较方法(Scala.math包下):Ordered特质混入Java的Comparable接口,它定义了相同类型间的比较方式,但这种内部比较方式是单一的;
trait Ordered[A] extends Any with java.lang.Comparable[A]......
-
Ordering特质混入Comparator接口,它是提供第三方比较器,可以自定义多种比较方式,也是使用比较多的,灵活解耦合。
trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable ......
-
使用Ordered特质和用Ordering特质进行排序操作
package com.scala.code import scala.util.Sorting case class Project(tag:String, score:Int) extends Ordered[Project] def compare(pro:Project ) = tag.compareTo(pro.tag) object OrderedDemo object OrderedDemo def main(args: Array[String]): Unit = val list = List(Project("hadoop",60), Project("flink",90), Project("hive",70),Project("spark",80)) println(list.sorted) val pairs = Array(("a", 7, 2), ("c", 9, 1), ("b", 8, 3)) // Ordering.by[(Int,Int,Double),Int](_._2)表示从Tuple3转到Int型 // 并按此Tuple3中第二个元素进行排序 Sorting.quickSort(pairs)(Ordering.by[(String, Int, Int), Int](_._2)) println(pairs.toBuffer)
模式匹配和样例类
模式匹配
-
Scala没有Java中的switch case,它有一个更加强大的模式匹配机制,可以应用到很多场合。
-
Scala的模式匹配可以匹配各种情况,比如变量的类型、集合的元素、有值或无值。
模式匹配的基本语法结构:变量 match case 值 => 代码
-
模式匹配match case中,只要有一个case分支满足并处理了,就不会继续判断下一个case分支了,不需要使用break语句。这点与Java不同,Java的switch case需要用break阻止。如果值为下划线,则代表不满足以上所有情况的时候如何处理。
-
模式匹配match case最基本的应用,就是对变量的值进行模式匹配。match是表达式,与if表达式一样,是有返回值的。除此之外,Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配。
字符和字符串匹配
def main(args: Array[String]): Unit =
val charStr = '6'
charStr match
case '+' => println("匹配上了加号")
case '-' => println("匹配上了减号")
case '*' => println("匹配上了乘号")
case '/' => println("匹配上了除号")
//注意:不满足以上所有情况,就执行下面的代码
case _ => println("都没有匹配上,我是默认值")
def main(args: Array[String]): Unit =
val arr = Array("hadoop", "zookeeper", "spark")
val name = arr(Random.nextInt(arr.length))
name match
case "hadoop" => println("大数据分布式存储和计算框架...")
case "zookeeper" => println("大数据分布式协调服务框架...")
case "spark" => println("大数据分布式内存计算框架...")
case _ => println("我不认识你...")
守卫式匹配
// 所谓守卫就是添加if语句
object MatchDemo
def main(args: Array[String]): Unit =
//守卫式
val character = '*'
val num = character match
case '+' => 1
case '-' => 2
case _ if character.equals('*') => 3
case _ => 4
println(character + " " + num)
匹配类型
-
Scala的模式匹配还有一个强大的功能,它可以直接匹配类型,而不是值。这一点是Java的switch case做不到的
-
匹配类型的语法:case 变量 : 类型 => 代码,而不是匹配值的“case 值 => 代码”这种语法。
package com.scala.code object pipeleixing def main(args: Array[String]): Unit = val a = 3 val obj = if(a == 1) 1 else if(a == 2) "2" else if(a == 3) BigInt(3) else if(a == 4) Map("aa" -> 1) else if(a == 5) Map(1 -> "aa") else if(a == 6) Array(1, 2, 3) else if(a == 7) Array("aa", 1) else if(a == 8) Array("aa") val r1 = obj match case x: Int => x case s: String => s.toInt // case BigInt => -1 //不能这么匹配 case _: BigInt => Int.MaxValue case m: Map[String, Int] => "Map[String, Int]类型的Map集合" case m: Map[_, _] => "Map集合" case a: Array[Int] => "It's an Array[Int]" case a: Array[String] => "It's an Array[String]" case a: Array[_] => "It's an array of something other than Int" case _ => 0 println(r1 + ", " + r1.getClass.getName)
匹配数组、元组、集合
def main(args: Array[String]): Unit =
val arr = Array(0, 3, 5)
//对Array数组进行模式匹配,分别匹配:
//带有指定个数元素的数组、带有指定元素的数组、以某元素开头的数组
arr match
case Array(0, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
//匹配数组以1开始作为第一个元素
case Array(1, _*) => println("1 ...")
case _ => println("something else")
val list = List(3, -1)
//对List列表进行模式匹配,与Array类似,但是需要使用List特有的::操作符
//构造List列表的两个基本单位是Nil和::,Nil表示为一个空列表
//tail返回一个除了第一元素之外的其他元素的列表
//分别匹配:带有指定个数元素的列表、带有指定元素的列表、以某元素开头的列表
list match
case x :: y :: Nil => println(s"x: $x y: $y")
case 0 :: Nil => println("only 0")
case 1 :: tail => println("1 ...")
case _ => println("something else")
val tuple = (1, 3, 7)
tuple match
case (1, x, y) => println(s"1, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
样例类
-
case class样例类是Scala中特殊的类。当声明样例类时,以下事情会自动发生:
- 主构造函数接收的参数通常不需要显式使用var或val修饰,Scala会自动使用val修饰
- 自动为样例类定义了伴生对象,并提供apply方法,不用new关键字就能够构造出相应的对象
- 将生成toString、equals、hashCode和copy方法,除非显示的给出这些方法的定义
- 继承了Product和Serializable这两个特质,也就是说样例类可序列化和可应用Product的方法
-
case class是多例的,后面要跟构造参数,case object是单例的,此外,case class样例类中可以添加方法和字段,并且可用于模式匹配。
package com.scala.code // 样例类 class Amount //定义样例类Dollar,继承Amount父类 case class Dollar(value: Double) extends Amount //定义样例类Currency,继承Amount父类 case class Currency(value: Double, unit: String) extends Amount //定义样例对象Nothing,继承Amount父类 case object Nothing extends Amount object ScalaDemo38 def main(args: Array[String]): Unit = judgeIdentity(Dollar(10.0)) judgeIdentity(Currency(20.2,"100")) judgeIdentity(Nothing) //自定义方法,模式匹配判断amt类型 def judgeIdentity(amt: Amount): Unit = amt match case Dollar(value) => println(s"$value") case Currency(value, unit) => println(s"Oh noes,I got $unit") case Nothing =
以上是关于Scala——(继承,特质,模式匹配和样例类)的主要内容,如果未能解决你的问题,请参考以下文章