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中的不包含在任何方法中的代码。
构造器以如下顺序执行:
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。它们之间的区别在于:
在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中特殊的类。当声明样例类时,以下事情会自动发生:
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 => println("Oh,GOD!")
}
}
}
Option与模式匹配
Scala Option选项类型用来表示一个值是可选的,有值或无值。
Option[T] 是一个类型为 T 的可选值的容器,可以通过get()函数获取Option的值。如果值存在,Option[T] 就是一个 Some。如果不存在,Option[T] 就是对象 None 。
Option通常与模式匹配结合使用,用于判断某个变量是有值还是无值。
package com.scala.code
object OptionMatch {
val grades = Map("jacky" -> 90, "tom" -> 80, "jarry" -> 95)
def getGrade(name: String): Unit = {
val grade = grades.get(name)
grade match {
case Some(grade) => println("成绩:" + grade)
case None => println("没有此人成绩!")
}
}
def main(args: Array[String]): Unit = {
getGrade("jacky")
getGrade("张三")
}
}