有时候,我们设计的程序,要根据变量的类型来执行对应的逻辑。
在Scala中,如何来进行类型判断呢?
有两种方式:
scala中对象提供isInstanceOf
和asInstanceOf
方法。
用法:
// 判断对象是否为指定类型
val trueOrFalse:Boolean = 对象.isInstanceOf[类型]
// 将对象转换为指定类型
val 变量 = 对象.asInstanceOf[类型]
示例:
class Person
class Student extends Person
object Main3 {
def main(args: Array[String]): Unit = {
val s1 = new Student
// 判断s1是否为Student类型
if(s1.isInstanceOf[Student]) {
// 将s1转换为Student类型
val s2 = s1.asInstanceOf[Student]
println(s2) // test.day01$Student@13805618
}
}
}
isInstanceOf 只能判断对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象。如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 。
用法:
示例:
class Person4
class Student4 extends Person4
object Student4{
def main(args: Array[String]) {
val p:Person4=new Student4
//判断p是否为Person4类的实例
println(p.isInstanceOf[Person4])//true
//判断p的类型是否为Person4类
println(p.getClass == classOf[Person4])//false
//判断p的类型是否为Student4类
println(p.getClass == classOf[Student4])//true
}
}
如果类的某个成员在当前类中的定义是不包含完整的,它就是一个抽象类
不完整定义有两种情况:
用法:
// 定义抽象类
abstract class 抽象类名 {
// 定义抽象字段
val 抽象字段名:类型
// 定义抽象方法
def 方法名(参数:参数类型,参数:参数类型...):返回类型
}
案例:求面积
// 创建形状抽象类
abstract class Shape {
def area:Double
}
// 创建正方形类
class Square(var edge:Double /*边长*/) extends Shape {
// 实现父类计算面积的方法
override def area: Double = edge * edge
}
// 创建长方形类
class Rectangle(var length:Double /*长*/, var width:Double /*宽*/) extends Shape {
override def area: Double = length * width
}
// 创建圆形类
class Cirle(var radius:Double /*半径*/) extends Shape {
override def area: Double = Math.PI * radius * radius
}
object Main6 {
def main(args: Array[String]): Unit = {
val s1:Shape = new Square(2)
val s2:Shape = new Rectangle(2,3)
val s3:Shape = new Cirle(2)
println(s1.area)
println(s2.area)
println(s3.area)
}
}
语法:
abstract class 抽象类 {
val/var 抽象字段:类型
}
示例:
// 定义一个人的抽象类
abstract class Person6 {
// 没有初始化的val字段就是抽象字段
val WHO_AM_I:String
}
class Student6 extends Person6 {
override val WHO_AM_I: String = "学生"
}
class Policeman6 extends Person6 {
override val WHO_AM_I: String = "警察"
}
object Main6 {
def main(args: Array[String]): Unit = {
val p1 = new Student6
val p2 = new Policeman6
println(p1.WHO_AM_I)
println(p2.WHO_AM_I)
}
}
匿名内部类是没有名称的子类,直接用来创建实例对象。Spark的源代码中有大量使用到匿名内部类。
scala中的匿名内部类使用与Java一致。
语法:
val/var 变量名 = new 类/抽象类 {
// 重写方法
}
示例:
abstract class Person7 {
def sayHello:Unit
}
object Main7 {
def main(args: Array[String]): Unit = {
// 直接用new来创建一个匿名内部类对象
val p1 = new Person7 {
override def sayHello: Unit = println("我是一个匿名内部类")
}
p1.sayHello
}
}
概念:scala中没有Java中的接口(interface),替代的概念是——特质
定义:
trait
关键字语法:
trait 名称 {
// 抽象字段
// 抽象方法
}
继承特质:
class 类 extends 特质1 with 特质2 {
// 字段实现
// 方法实现
}
extends
来继承trait(scala不论是类还是特质,都是使用extends关键字)with
关键字 trait Logger {
// 抽象方法
def log(message:String)
}
class ConsoleLogger extends Logger {
override def log(message: String): Unit = println("控制台日志:" + message)
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.log("这是一条日志")
}
trait MessageSender {
def send(msg:String)
}
trait MessageReceive {
def receive():String
}
class MessageWorker extends MessageSender with MessageReceive {
override def send(msg: String): Unit = println(s"发送消息:${msg}")
override def receive(): String = "你好!我叫一个好人!"
}
def main(args: Array[String]): Unit = {
val worker = new MessageWorker
worker.send("hello")
println(worker.receive())
}
trait Logger {
def log(message:String)
}
object ConsoleLogger extends Logger {
override def log(message: String): Unit = println("控制台消息:" + message)
}
def main(args: Array[String]): Unit = {
ConsoleLogger.log("程序退出!")
}
trait LoggerDetail {
// 在trait中定义具体方法
def log(msg:String) = println(msg)
}
class UserService extends LoggerDetail {
def add() = log("添加用户")
}
object MethodInTrait {
def main(args: Array[String]): Unit = {
val userService = new UserService
userService.add()
}
}
在trait中可以定义具体字段和抽象字段
继承trait的子类自动拥有trait中定义的字段
字段直接被添加到子类中
示例:
trait Logger {
val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm")
def log(msg:String)
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = {
val info = s"${sdf.format(new Date())}:控制台消息:${msg}"
println(info)
}
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger()
logger.log("NullPointerException")
}
要实现以下需求:
在一个特质中,具体方法依赖于抽象方法,而抽象方法可以放到继承trait的子类中实现,这种设计方式也称为模板模式
示例:
trait Logger {
def log(msg:String)
def info(msg:String) = log("INFO:" + msg)
def warn(msg:String) = log("WARN:" + msg)
def error(msg:String) = log("ERROR:" + msg)
}
class ConsoleLogger extends Logger {
override def log(msg: String): Unit = {
println(msg)
}
}
def main(args: Array[String]): Unit = {
val logger = new ConsoleLogger
logger.info("信息日志")
logger.warn("警告日志")
logger.error("错误日志")
}
scala中可以将trait混入到对象中,就是将trait中定义的方法、字段添加到一个对象中
语法:
val/var 对象名 = new 类 with 特质
示例:
trait Logger {
def log(msg:String) = println(msg)
}
class UserService
def main(args: Array[String]): Unit = {
val service = new UserService with Logger
service.log("混入的方法")
}
我们如果要开发一个支付功能,往往需要执行一系列的验证才能完成支付。例如:
如果将来因为第三方接口支付的调整,需要增加更多的校验规则,此时如何不修改之前的校验代码,来实现扩展呢?
责任链模式
trait调用链
类继承了多个trait后,可以依次调用多个trait中的同一个方法,只要让多个trait中的同一个方法在最后都依次执行super关键字即可。类中调用多个tait中都有这个方法时,首先会从最右边的trait方法开始执行,然后依次往左执行,形成一个调用链条。
示例:
实现一个模拟支付过程的调用链
步骤
object _08TraitDemo {
// 1. 定义一个trait(HandlerTrait)
trait HandlerTrait {
def handle(data:String) = {
println("处理支付数据...")
}
}
// 2. 定义两个trait(数据校验、签名校验)
trait DataValidateTrait extends HandlerTrait {
override def handle(data: String): Unit = {
println("数据校验...")
super.handle(data)
}
}
trait SignatureValidateTrait extends HandlerTrait {
override def handle(data: String): Unit = {
println("签名校验...")
super.handle(data)
}
}
// 3. 定义一个支付服务类,继承数据校验、签名校验的trait
class PayService extends DataValidateTrait with SignatureValidateTrait {
override def handle(data: String): Unit = {
println("准备支付...")
super.handle(data)
}
}
// 4. 创建支付服务对象,调用支付方法
def main(args: Array[String]): Unit = {
val service = new PayService
service.handle("支付数据")
}
}
程序运行输出如下:
准备支付...
签名校验...
数据校验...
处理支付数据...
从左到右
依次执行trait的构造器示例:
trait Logger {
println("执行Logger构造器")
}
trait MyLogger extends Logger {
println("执行MyLogger构造器")
}
trait TimeLogger extends Logger {
println("执行TimeLogger构造器")
}
class Person{
println("执行Person构造器")
}
class Student extends Person with TimeLogger with MyLogger {
println("执行Student构造器")
}
def main(args: Array[String]): Unit = {
new Student
}
// 程序运行输出如下:
// 执行Person构造器
// 执行Logger构造器
// 执行TimeLogger构造器
// 执行MyLogger构造器
// 执行Student构造器
trait也可以继承class的。特质会将class中的成员都继承下来。
示例:
class MyUtil {
def printMsg(msg:String) = println(msg)
}
trait Logger extends MyUtil {
def log(msg:String) = printMsg("Logger:" + msg)
}
class Person extends Logger {
def sayHello() = log("你好")
}
def main(args: Array[String]): Unit = {
val person = new Person
person.sayHello()
}
样例类是一种特殊类,它可以用来快速定义一个用于保存数据的类(类似于Java POJO类),在后续要学习并发编程和spark、flink这些框架也都会经常使用它。
语法:
case class 样例类名([var/val] 成员变量名1:类型1, 成员变量名2:类型2, 成员变量名3:类型3)
作用:
示例1:定义一个样例类
object _01CaseClassDemo {
case class Person(name:String, age:Int)
def main(args: Array[String]): Unit = {
val zhangsan = Person("张三", 20)
println(zhangsan)
}
}
示例2:可变成员变量
object _02CaseClassDemo {
case class Person(var name:String, var age:Int)
def main(args: Array[String]): Unit = {
val zhangsan = Person("张三", 20)
zhangsan.age = 23
println(zhangsan)
}
}
默认已被实现
apply方法可以让我们快速地使用类名来创建对象。参考以下代码:
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
}
}
默认已被实现
toString返回样例类名称(成员变量1, 成员变量2, 成员变量3....),我们可以更方面查看样例类的成员
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi) // CasePerson(李四,21)
println(lisi.toString) //CasePerson(李四,21)
}
}
注:直接打印lisi相当于调用了toString方法
样例类自动实现了equals方法,可以直接使用==比较两个样例类是否相等,即所有的成员变量是否相等
示例:
object day04 {
case class CasePerson(name:String, age:Int)
def main(args: Array[String]): Unit = {
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 21)
// 比较两个对象是否相等
println(lisi1 == lisi2) // true
println(lisi1.equals(lisi2)) // true
// 比较两个对象是否是同一个对象
println(lisi1.eq(lisi2)) // false
}
}
样例类自动实现了hashCode方法,如果所有成员变量的值相同,则hash值相同,只要有一个不一样,则hash值不一样。
object day04 {
case class CasePerson(name:String, age:Int)
def main(args: Array[String]): Unit = {
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 22)
val lisi3 = CasePerson("李四", 21)
// 打印哈希值,值相同时,哈希值也相同
println(lisi1.hashCode()) // 718456491
println(lisi2.hashCode()) // 629649205
println(lisi3.hashCode()) // 718456491
}
}
样例类实现了copy方法,可以快速创建一个相同的实例对象,可以使用带名参数指定给成员进行重新赋值
object day04 {
case class CasePerson(name:String, age:Int)
def main(args: Array[String]): Unit = {
val lisi1 = CasePerson("李四", 21)
val wangwu = lisi1.copy(name="王五")
println(wangwu) // CasePerson(王五,21)
}
}
它主要用在两个地方:
定义枚举:
使用case object可以创建样例对象。样例对象是单例的,而且它没有主构造器
语法:
case object 样例对象名
示例:
object day04 {
trait Sex /*定义一个性别特质*/
case object Male extends Sex // 定义一个样例对象并实现了Sex特质
case object Female extends Sex
case class Person(name:String, sex:Sex)
def main(args: Array[String]): Unit = {
val zhangsan = Person("张三", Male)
val lisi = Person("lisi", Female)
println(zhangsan)//Person(张三,Male)
println(lisi)//Person(lisi,Male)
println(Male) // Male
println(Female) // Female
}
}
scala中有一个非常强大的模式匹配机制,可以应用在很多场景:
在Java中,有switch关键字,可以简化if条件判断语句。在scala中,可以使用match表达式替代。
语法:
变量 match {
case "常量1" => 表达式1
case "常量2" => 表达式2
case "常量3" => 表达式3
case _ => 表达式4 // 默认匹配
}
示例:
println("请输出一个词:")
// StdIn.readLine表示从控制台读取一行文本
val name = StdIn.readLine()
val result = name match {
case "hadoop" => "大数据分布式存储和计算框架"
case "zookeeper" => "大数据分布式协调服务框架"
case "spark" => "大数据分布式内存计算框架"
case _ => "未匹配"
}
println(result)
除了像Java中的switch匹配数据之外,match表达式还可以进行类型匹配。如果我们要根据不同的数据类型,来执行不同的逻辑,也可以使用match表达式来实现。
语法:
变量 match {
case 类型1变量名: 类型1 => 表达式1
case 类型2变量名: 类型2 => 表达式2
case 类型3变量名: 类型3 => 表达式3
...
case _ => 表达式4
}
示例:
定义一个变量为Any类型,然后分别给其赋值为"hadoop"、1、1.0
object _02MatchDemo {
def main(args: Array[String]): Unit = {
// 1. 定义Any类型的变量
// 分别赋值:"hadoop"、1、1.0
val a:Any = "hadoop"
// val a:Any = 1
// val a:Any = 1.0
// val a:Any = null // Null类型的实例
// 2. 使用模式匹配来匹配数据类型
a match {
case _:String => println(s"字符串类型")
case x:Int => println(s"${x}是整形")
case x:Double => println(s"${x}是双精度浮点型")
case _ => println("未匹配")
}
}
}
注:如果case表达式中无需使用到匹配到的变量,可以使用下划线代代替
在scala中,可以使用守卫来简化多个case——也就是在case语句中添加if条件判断。
示例:
val a = StdIn.readInt()
a match {
case _ if a >= 0 && a <= 3 => println("[0-3]")
case _ if a >= 4 && a <= 8 => println("[3-8]")
case _ => println("未匹配")
}
scala可以使用模式匹配来匹配样例类,从而可以快速获取样例类中的成员数据。后续,我们在开发Akka案例时,还会用到。
示例:
// 1. 创建两个样例类
case class Person(name:String, age:Int)
case class Order(id:String)
def main(args: Array[String]): Unit = {
// 2. 创建样例类对象,并赋值为Any类型
val zhangsan:Any = Person("张三", 20)
val order1:Any = Order("001")
// 3. 使用match...case表达式来进行模式匹配
// 获取样例类中成员变量
order1 match {
case Person(name, age) => println(s"姓名:${name} 年龄:${age}")
case Order(id) => println(s"ID为:${id}")
case _ => println("未匹配")
}
}
依次修改以下三个数组,并查看结果:
Array(1,x,y) // 以1开头,后续的两个元素不固定
Array(0) // 只匹配一个0元素的元素
Array(0, ...) // 可以任意数量,但是以0开头
object _05MatchDemo {
def main(args: Array[String]): Unit = {
// 1. 定义三个数组,用于匹配
val array1 = Array(1, 2, 3)
val array2 = Array(0)
val array3 = Array(0, 1, 2, 3, 4, 5)
// 2. 使用match...case 来进行匹配
array3 match {
case Array(1, x, y) => println(s"匹配三个元素的数组,第一个元素为1,${x}, ${y}")
case Array(0) => println(s"匹配一个元素的数组,元素值为0")
case Array(0, _*) => println("第一个元素为0,数量不固定的数组")
}
}
}
依次修改以下三个数组,并查看结果:
List(0) // 只保存0一个元素的列表
List(0,...) // 以0开头的列表,数量不固定
List(x,y) // 只包含两个元素的列表
object _06MatchDemo {
def main(args: Array[String]): Unit = {
// 1. 创建三个不同的List
val list1 = List(0)
val list2 = List(0, 1, 1, 2, 3, 3)
val list3 = List(10, 11)
// 2. 使用match..case来进行模式匹配
list3 match {
case 0 :: Nil => println("只有一个元素且元素为0的列表")
case 0 :: tail => println("第一个元素为0且数量不固定的列表")
case x :: y :: Nil => println(s"列表只有两个元素:${x},${y}")
}
}
}
依次修改以下三个数组,并查看结果:
(1, x, y) // 以1开头的、一共三个元素的元组
(x, y, 5) // 一共有三个元素,最后一个元素为5的元组
object _07MatchDemo {
def main(args: Array[String]): Unit = {
// 1. 创建两个元组用于模式匹配
val tuple1 = (1, 3, 4)
val tuple2 = (3, 4, 5)
// 2. 使用match...case来进行匹配
tuple2 match {
case (1, x, y) => println(s"匹配三个元素且第一个元素为1的元组")
case (x, y, 5) => println(s"匹配三个元素且最后一个元素为5的元组")
}
}
}
在定义变量的时候,可以使用模式匹配快速获取数据
示例1: 获取数组中的元素
// 生成包含0-10数字的数组,使用模式匹配分别获取第二个、第三个、第四个元素
object _08MatchDemo {
def main(args: Array[String]): Unit = {
// 1. 生成数组0-10
val array = (0 to 10).toArray
// 2. 模式匹配获取第二个、三个、四个
val Array(_, x, y, z, _*) = array
println(x, y, z)
}
}
示例2:获取List中的数据
// 生成包含0-10数字的列表,使用模式匹配分别获取第一个、第二个元素
object _09MatchDemo {
def main(args: Array[String]): Unit = {
// 1. 生成0-10列表
val list = (0 to 10).toList
// 2. 在定义声明变量的时候,使用模式匹配,来匹配第一个、第二个元素
val x :: y :: tail = list
println(x, y)
}
}
使用Option类型,可以用来有效避免空引用(null)异常。也就是说,将来我们返回某些数据时,可以返回一个Option类型来替代。
定义:
scala中,Option类型来表示可选值。这种类型的数据有两种形式:
Some(x):表示实际的值
源码:
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
None:表示没有值
源码:
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}
示例1:
/**
* 定义一个两个数相除的方法,使用Option类型来封装结果
* @param a 参数1
* @param b 参数2
* @return Option包装Double类型
*/
object _07OptionDemo {
// 1. 定义一个相除的方法,会使用Option来封装数据
def div(x:Double, y:Double):Option[Double] = {
if(y == 0) {
// 表示没有数据
None
}
else {
val result = x / y
// Some表示有数据
Some(result)
}
}
// 2. 可以使用模式匹配来判断是否有值
def main(args: Array[String]): Unit = {
val some = div(10.0, 2.0)
val none = div(10.0, 0)
// none match {
// case Some(x) => println(x)
// case None => println("除零异常")
// }
// 使用getOrElse来获取Option类型的值,当值为None的时候指定默认值为0
println(some.getOrElse(0))// 5.0
println(none.getOrElse(0)) // 0
}
}
偏函数可以提供了简洁的语法,可以简化函数的定义。配合集合的函数式编程,可以让代码更加优雅。
定义:
偏函数被包在花括号内没有match的一组case语句是一个偏函数
偏函数是PartialFunction[A, B]的一个实例
示例1:
object day05 {
def main(args: Array[String]): Unit = {
// func1是一个输入参数为Int类型,返回值为String类型的偏函数
val func1: PartialFunction[Int, String] = {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
println(func1(2))// 二
}
}
示例2:
val list = (1 to 10).toList
val list2 = list.map{
case x if x >= 1 && x <= 3 => "[1-3]"
case x if x >= 4 && x <= 8 => "[4-8]"
case x if x > 8 => "(8-*]"
}
println(list2)
//List([1-3], [1-3], [1-3], [4-8], [4-8], [4-8], [4-8], [4-8], (8-*], (8-*])
定义:
reegx类
scala中提供了Regex类来定义正则表达式
要构造一个RegEx对象,直接使用String类的r方法即可
建议使用三个双引号来表示正则表达式,不然就得对正则中的反斜杠来进行转义
语法:
val regEx = """正则表达式""".r
findAllMatchIn方法
具体语法查看文档:
https://www.runoob.com/regexp/regexp-syntax.html
示例1:
定义一个正则表达式,来匹配邮箱是否合法
val r = """.+@.+\..+""".r
val eml1 = "qq12344@163.com"
val eml2 = "qq12344@.com"
if(r.findAllMatchIn(eml1).size > 0) {
println(eml1 + "邮箱合法")
}
else {
println(eml1 + "邮箱不合法")
}
if(r.findAllMatchIn(eml2).size > 0) {
println(eml2 + "邮箱合法")
}
else {
println(eml2 + "邮箱不合法")
}
示例2
找出以下列表中的所有不合法的邮箱
object day05 {
def main(args: Array[String]): Unit = {
val emlList = List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com")
val regex = """.+@.+\..+""".r
val invalidEmlList = emlList.filter {
case x =>if (regex.findAllMatchIn(x).size < 1) true else false
}
println(invalidEmlList)
// 123afadff.com
}
}
示例3
使用正则表达式进行模式匹配,匹配出来邮箱运营商的名字。例如:邮箱zhansan@163.com,需要将163匹配出来
object day05 {
def main(args: Array[String]): Unit = {
// 使用括号表示一个分组
val regex = """.+@(.+)\..+""".r
val emlList =
List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com")
val emlCmpList = emlList.map {
case x@regex(company) => s"${x} => ${company}"
case x => x + "=>未知"
}
println(emlCmpList)
// List(38123845@qq.com => qq, a1da88123f@gmail.com => gmail, zhansan@163.com => 163, 123afadff.com=>未知)
}
}
语法:
try {
// 代码
}
catch {
case ex:异常类型1 => // 代码
case ex:异常类型2 => // 代码
}
finally {
// 代码
}
示例:
try {
val i = 10 / 0
println("你好!")
} catch {
case ex: Exception => println(ex.getMessage)
}
示例:
def main(args: Array[String]): Unit = {
throw new Exception("这是一个异常")
}
Exception in thread "main" java.lang.Exception: 这是一个异常
at ForDemo$.main(ForDemo.scala:3)
at ForDemo.main(ForDemo.scala)
scala不需要在方法上声明要抛出的异常,它已经解决了再Java中被认为是设计失败的检查型异常。
我们之前已经使用过scala中非常强大的模式匹配功能了,通过模式匹配,我们可以快速匹配样例类中的成员变量。例如:
// 1. 创建两个样例类
case class Person(name:String, age:Int)
case class Order(id:String)
def main(args: Array[String]): Unit = {
// 2. 创建样例类对象,并赋值为Any类型
val zhangsan:Any = Person("张三", 20)
val order1:Any = Order("001")
// 3. 使用match...case表达式来进行模式匹配
// 获取样例类中成员变量
order1 match {
case Person(name, age) => println(s"姓名:${name} 年龄:${age}")
case Order(id1) => println(s"ID为:${id1}")
case _ => println("未匹配")
}
}
注:那是不是所有的类都可以进行这样的模式匹配呢?答案是:不可以的
要支持模式匹配,必须要实现一个提取器。
样例类自动实现了apply、unapply方法
定义提取器
之前我们学习过了,实现一个类的伴生对象中的apply方法,可以用类名来快速构建一个对象。伴生对象中,还有一个unapply方法。与apply相反,unapply是将该类的对象,拆解为一个个的元素。
语法:
def unapply(stu:Student):Option[(类型1, 类型2, 类型3...)] = {
if(stu != null) {
Some((变量1, 变量2, 变量3...))
}
else {
None
}
}
示例:
class Student(var name:String, var age:Int)
object Student {
def apply(name:String, age:Int) = {
new Student(name, age)
}
def unapply(student:Student) = {
val tuple = (student.name, student.age)
Some(tuple)
}
else{
None
}
}
def main(args: Array[String]): Unit = {
val zhangsan = Student("张三", 20)
zhangsan match {
case Student(name, age) => println(s"${name} => ${age}")
}
}
语法:
def 方法名[泛型名称](..) = {
//...
}
示例:
不考虑泛型实现:
def getMiddle(arr:Array[Int]) = arr(arr.length / 2)
def main(args: Array[String]): Unit = {
val arr1 = Array(1,2,3,4,5)
println(getMiddle(arr1))
}
加入泛型支持
def getMiddleElement[T](array:Array[T]) =array(array.length / 2)
def main(args: Array[String]): Unit = {
println(getMiddleElement(Array(1, 2, 3, 4, 5)))
println(getMiddleElement(Array("a", "b", "c", "d", "e")))
}
语法:
class 类[T](val 变量名: T)
示例:
case class Pair[T](var a:T, var b:T)
def main(args: Array[String]): Unit = {
val pairList = List(
Pair("Hadoop", "Storm"),
Pair("Hadoop", 2008),
Pair(1.0, 2.0),
Pair("Hadoop", Some(1.9))
)
println(pairList)
}
我们在定义方法/类的泛型时,限定必须从哪个类继承、或者必须是哪个类的父类。此时,就需要使用到上下界。
使用<: 类型名
表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承
语法:
[T <: 类型]
示例:
class Person
class Student extends Person
def demo[T <: Person](a:Array[T]) = println(a)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Student))
// 编译出错,必须是Person或其的子类
// demo(Array("hadoop"))
}
上界是要求必须是某个类的子类,或者必须从某个类继承,而下界是必须是某个类的父类(或本身)
语法:
[T >: 类型]
注:如果类既有上界、又有下界。下界写在前面,上界写在后面
示例:
class Person
class Policeman extends Person
class Superman extends Policeman
def demo[T >: Policeman](array:Array[T]) = println(array)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Policeman))
Policeman或其父类
// 编译出错:Superman是Policeman的子类,我们指定了泛型必须是Policeman或其父类
// demo(Array(new Superman))
}
spark的源代码中大量使用到了协变、逆变、非变,学习该知识点对我们将来阅读spark源代码很有帮助。
如下存在类型转换问题:
class Pair[T]
object Pair {
def main(args: Array[String]): Unit = {
val p1 = Pair("hello")
// 编译报错,无法将p1转换为p2
val p2:Pair[AnyRef] = p1
println(p2)
}
}
如何让带有泛型的类支持类型转换呢?
语法:
class Pair[T]{}
概念:泛型变量的值可以是本身类型或者其子类型
语法:
class Pair[+T]
概念:泛型变量的值可以是其本身或是其父类
语法格式
class Pair[-T]
示例:
class Super
class Sub extends Super
//非变(默认)
class Temp1[T]
//协变
class Temp2[+T]
//逆变
class Temp3[-T]
def main(args: Array[String]): Unit = {
val a:Temp1[Sub] = new Temp1[Sub]
// 编译报错
// 非变不允许任何的转换
//val b:Temp1[Super] = a
// 协变泛型允许将子类赋值给父类
val c: Temp2[Sub] = new Temp2[Sub]
val d: Temp2[Super] = c
// 逆变泛型允许将父类赋值给子类
val e: Temp3[Super] = new Temp3[Super]
val f: Temp3[Sub] = e
}
定义:视图界定是上界和下界的一种扩展
期望函数可以接受上界和下界规定的类型外,还可以接受通过隐式转换转换过去的类型
语法:
用 % 来表示
def a[T<%String](x:T,y:T) = x + " ***** " + y
// a(1,2)//报错
implicit def b(n:Int):String = n.toString
println(a(b(1),b(2)))
//输出:1 ***** 2
代表:
注:相同类型的转换只能定义一次,比如Int->String,如果按照上述案例再次进行定义,则会报错
内容来源于网络,如有侵权,请联系作者删除!