Scala程序设计基础(四)

异常处理

  • 类似Java,使用try..catch捕获处理异常
  • 不同的是catch中使用模式匹配的思想
	try {
	    // 代码
	}
	catch {
	    case ex:异常类型1 => // 代码
	    case ex:异常类型2 => // 代码
	}
	finally {	// 不管是否出现异常都会执行
	    // 代码
	}
  • 同样,可以使用throw抛出异常

提取器

  • 是一个带有unapply方法的对象
  • 从传递给它的对象中提取出构造该对象的参数
    在这里插入图片描述
  • 类似于apply()方法,需要在伴生对象中使用
class Student {
  var name:String = _   // 姓名
  var age:Int = _       // 年龄
  
  // 实现一个辅助构造器
  def this(name:String, age:Int) = {
    this()	// 父类构造方法
    this.name = name
    this.age = age
  }
}

object Student {	// 伴生对象
  // 构造器
  def apply(name:String, age:Int): Student = new Student(name, age)

  // 解构器
  def unapply(arg: Student): Option[(String, Int)] = Some(arg.name, arg.age))
}

object extractor_DEMO {
  def main(args: Array[String]): Unit = {
    val zhangsan = Student("张三", 20)	// 使用伴生对象的apply方法直接实例化

    zhangsan match {	// 自动实现实例类的解构方法
      case Student(name, age) => println(s"姓名:$name 年龄:$age")
      case _ => println("未匹配")
    }
  }
}

泛型

  • 泛型用于指定方法或类可以接受任意类型参数,参数在实际使用时才被确定
  • 使用泛型可以使得类或方法具有更强的通用性,泛型无处不在
  • 使用方括号[ ]来定义类型参数(注意区分类型与类型参数)
  def getMiddle[A](arr:Array[A]) = arr(arr.length / 2)

  def main(args: Array[String]): Unit = {
    val arr1 = Array(1,2,3,4,5)
    val arr2 = Array("a", "b", "c", "d", "f")

    println(getMiddle[Int](arr1))	// 传参时确定类型
    println(getMiddle[String](arr2))

    // 简写方式
    println(getMiddle(arr1))
    println(getMiddle(arr2))
  }
  • 泛型类
// 类名后面的方括号,就表示这个类可以使用两个类型、分别是T和S
// 这个名字可以任意取,可以叫W或者C
class Pair[T, S](val first: T, val second: S)

case class People(var name:String, val age:Int)

object Pair {
  def main(args: Array[String]): Unit = {

  val p1 = new Pair[String, Int]("张三", 10)
  val p2 = new Pair[String, String]("张三", "1988-02-19")
  val p3 = new Pair[People, People](People("张三", 20), People("李四", 30))
  }
}
  • 随着泛型还有一些其他概念:

上下界

  • 在指定泛型类型时,有时需要界定泛型类型的范围,而不是接收任意类型
  • 上下边界特性允许泛型类型是某个类的子类,或者是某个类的父类
//要控制Person只能和Person、Policeman聊天,但是不能和Superman聊天。此时,还需要给泛型添加一个下界。
//上下界
class Pair[T <: Person, S >: Policeman <:Person](val first: T, val second: S) {
  def chat(msg:String) = println(s"${first.name}对${second.name}说: $msg")
}

class Person(var name:String, val age:Int)
class Policeman(name:String, age:Int) extends Person(name, age)
class Superman(name:String) extends Policeman(name, -1)

object Pair {
  def main(args: Array[String]): Unit = {
//运行错误:第二个参数必须是Person的子类(包括本身)、Policeman的父类(包括本身)
    val p3 = new Pair[Person,Superman](new Person("张三", 20), new Superman("李四"))
    p3.chat("你好!")
  }
}

协变

  • class Pair[+T],这种情况是协变。类型B是A的子类型,那么Pair[B]可以认为是Pair[A]的子类型
  • 这种情况,类型参数的方向和类型的方向是一致的
class Pair[+T](a:T)

object Pair {
  def main(args: Array[String]): Unit = {
    val p1 = new Pair("hello")
    // String是AnyRef的子类
    val p2:Pair[AnyRef] = p1
    println(p2)
  }
}

逆变

  • class Pair[-T],这种情况是逆变。类型B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型
  • 这种情况,类型参数的方向和类型的方向是相反的

非变

  • class Pair[T],这种情况就是非变(默认),类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系
class Super
class Sub extends Super

//非变
class Temp1[A](title: String)
//协变
class Temp2[+A](title: String)
//逆变
class Temp3[-A](title: String)

object Covariance_demo {
  def main(args: Array[String]): Unit = {
    val a = new Sub()
    // 与Java同样,向上转型,不属于泛型
    val b:Super = a

    // 非变
    val t1:Temp1[Sub] = new Temp1[Sub]("测试")
    // 报错!默认不允许转换
    // val t2:Temp1[Super] = t1

    // 协变
    val t3:Temp2[Sub] = new Temp2[Sub]("测试")
    val t4:Temp2[Super] = t3	// 类似Java普通类向上转型
    
    // 逆变
    val t5:Temp3[Super] = new Temp3[Super]("测试")
    val t6:Temp3[Sub] = t5		// 类似Java普通类向下转型
  }
}

-更加详细的内容可以参考说明或者官方文档

隐式转换和隐式参数

  • 可以允许你手动指定,将某种类型的对象转换成其他类型的对象或者是给一个类增加方法
  • 隐式转换:使用 implicit 修饰的方法实现把一个原始类转换成目标类,进而可以调用目标类中的方法
  • 所有的隐式转换和隐式参数必须定义在一个object中
//todo:一个类隐式转换成具有相同方法的多个类

class C
class A(c:C) {
  def readBook(): Unit ={
    println("A说:好书好书...")
  }
}

class B(c:C){
  def readBook(): Unit ={
    println("B说:看不懂...")
  }
  def writeBook(): Unit ={
    println("B说:不会写...")
  }
}

object AB{
  // 将原始类C转换为目标类A B
  // 此时的C就有了A/B的能力
  implicit def C2A(c:C) = new A(c)
  implicit def C2B(c:C) = new B(c)
}

object B{
  def main(args: Array[String]) {
    //导包
    //1. import AB._ 会将AB类下的所有隐式转换导进来
    //2. import AB.C2A 只导入C类到A类的的隐式转换方法
    //3. import AB.C2B 只导入C类到B类的的隐式转换方法
    import AB._
    val c=new C

    //由于A类与B类中都有readBook(),只能导入其中一个,否则会冲突
    //c.readBook()

    //C类可以执行B类中的writeBook()  即C2B
    c.writeBook()
  }
}
  • 隐式参数:在函数或者方法中,定义一个用implicit修饰的参数,Scala会尝试找到一个指定类型的用implicit修饰的参数,即隐式值,传入方法
  • Scala会在两个范围内查找:一种是当前作用域内可见的val或var定义的隐式变量(导包);一种是隐式参数类型的伴生对象内的隐式值
//todo:隐式参数案例:员工领取薪水

object Company{
  // 在object中定义用implicit修饰的参数(隐式参数)
  // 注意:同一类型的隐式值只允许出现一次,否则会报错
  implicit val xxx="zhangsan"
  implicit val yyy=10000.00
  //implicit  val zzz="lisi"
}

class Boss {
  // 定义一个用implicit修饰的参数,类型为String
  // 注意参数匹配的类型   它需要的是String类型的隐式值
  def callName(implicit name:String):String={
    name+" is coming !"
  }

  // 定义一个用implicit修饰的参数,类型为Double
  // 注意参数匹配的类型    它需要的是Double类型的隐式值
  def getMoney(implicit money:Double):String={
    " 当月薪水:"+money
  }
}

object Boss extends App{
  //使用import导入定义好的隐式值,注意:必须先加载否则会报错
  import Company.xxx	// String类型的隐式值
  import Company.yyy	// Int类型的隐式值

  val boss = new Boss
  // Scala会尝试找到一个指定类型的用implicit修饰的参数,即隐式值,传入方法
  println(boss.callName+boss.getMoney)

}

结语

以上四篇基本介绍了在大数据开发过程中经常使用到的基本Scala语法,这并不是全面的语法总结。Scala是一门多范式编程语言,有很强的灵活性,掌握思想比较关键,后面会通过案例和源码的方式呈现基本原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章