scala注意点
一、方法中的注意点
1、方法的定义
def add(x:Int,y:Int):Int = x+y
def add1(x:Int,y:Int)=x+y
//与上面两种方法有区别,省略= 始终返回unit 此处值得注意
def add2(x:Int,y:Int) {x+y}
2、空参、无参方法的注意点
def add3=System.getProperty("user.name")
def add4()=System.getProperty("user.name")
//空参方法可以作为最终表达式出现,实际上是方法调用,只不过Scala规定空参方法的调用可以省略`()`。==但是无参方法不允许使用`()`调用,建议当成普通变量使用==
println(add3)
println(add3()) //不允许
println(add4,add4())
3、def与val的区别
/**
* val只会运行一次,初始化一次后就不执行方法体了 类似与java的static
* 只会在第一次调用时会执行方法体
* def每次都会运行,每次调用,都会执行方法体
* 最重要的一点:
* val是变量,而变量不需要调用就被初始化
* def是方法 ,执行的时机是调用的时候
*/
def add5 = {
println("hello1")
System.getProperty("user.name")
}
val add6 = {
println("hello2")
System.getProperty("user.name")
}
println(add5,add6,add5,add6)
结果为:
hello2
hello1
hello1
(等待,等待,等待,等待)
4、参数传递
参考文档
5、默认参数 和 可变参数
参考文档
二、函数
函数有返回值,但是呢不需要自己加返回值类型,会自动推断,自己加会出错
方法转函数
方法与函数的区别:
本质:最本质的区别是函数可以做为参数传递到方法中
三、scala集合
3.1数组
3.1.1、数组定义
- 使用new关键字创建一个定长数组,`var arr=new Array[Int](3)`
- 直接使用Array创建并初始化一个数组,注意不再使用new关键字。`var arr=Array(1,2,3)`
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hDBKrARj-1630938832073)(file://G:\BigData2021资料\scala\day2\Scala_02.assets\array01.png?lastModify=1622640578)]
3.1.2、定长数组Array
#1、数组常用方法
求和:sum
求最大值:max
求最小值:min
数组排序:sorted --默认升序,会返回一个新的数组,原来的数组长不会改变
可以自定义排序规则:sortWith --也是返回一个新的数组
object ArrayAlgorithm {
def main(args: Array[String]): Unit = {
val arr = Array(9,1,3,4,2,7,5,6,8)
println(arr.sum) // 数组求和:45
println(arr.max) // 数组求最大值:9
println(arr.min) // 数组求最小值:1
println(arr.sorted.toBuffer)// 数组排序:默认升序,返回一个新的数组
println(arr.sortWith(_<_).toBuffer)// 可以自定义排序规则,返回一个新的数组
}
}
#2、array遍历的常用操作:foreach map filter
val arr= (1 to 10).toArray
//遍历
for (i<-arr)
println(i*2)
//foreach 常用
arr.foreach((x:Int)=>{ if(x%2==0) println(x*2)})
//filter 实现偶数*2 传入一个条件,为true的元素保留下来
arr.filter((x:Int)=>x%2==0)
.foreach((x:Int)=>println(x*2))
//map 是对每一个元素进行处理并返回一个新的集合
val result=arr.map((x:Int)=>{if(x%2==0) x*2 else 0}).filter((x:Int)=>x>0)
result.foreach((x:AnyVal)=>println(x))
3.1.3、变长数组ArrayBuffer
#1、增加
+=
++=
append
insert
#2、删除
remove
trimEnd
trimStart
#3、遍历
增强for循环进行数组遍历
for(elem <- nums)
println(elem)
基于下标访问使用增强for循环进行数组遍历
for(i <- 0 until nums.length)
println(nums(i))
注意:ArrayBuffer无法使用 to
3.1.4、数组进阶
#1、创建二维数组
#2、创建不规则多维数组
3.2、元组(Tuple)
#1、元组的创建方式 -- 两种定义方式
1、val t = ("a","b",1)
2、val tt = new Tuple3(1, 3.14, "Fred")
#2、元组访问
使用`_1`,` _2`,`_3`等形式访问组元,注意下标==从1开始==
val value1 = tuple3._3
println(value1)
#3、元组遍历 -- 两种方式
方式一: 使用productIterator()方法
for (elem <- tuple1.productIterator) {
print(elem)
}
println()
方式二: foreach方法
tuple1.productIterator.foreach(i => println(i)) //foreach是高阶函数
#4、拉链操作
#5、拉链扩展
3.3、映射(map)
3.3.1、简单说明
在Scala中,把哈希表这种数据结构叫做映射。Scala中的Map存储的内容是键值对(key-value),Map区分可变Map (scala.collection.mutable.Map) 和不可变Map(scala.collection.immutable.Map) 。==不可变的Map(仅TreeMap)支持有序,而可变的Map是无序==的。
HashMap也是默认的Map类型
3.3.2、构建映射
//Map中的元素为二元组,可用两种方式表示
//方式一
("a",1)
//方式二
"a"->1
//实例如下
scala> val scores = Map("zhangsan"->90,"lisi"->80,"wangwu"->70)
scores: scala.collection.immutable.Map[String,Int] = Map(zhangsan -> 90, lisi -> 80, wangwu -> 70)
scala> val scores = Map(("zhangsan",90),("lisi",80),("wangwu",70))
scores: scala.collection.immutable.Map[String,Int] = Map(zhangsan -> 90, lisi -> 80, wangwu -> 70)
scala> val scores = scala.collection.mutable.Map(("zhangsan",90),("lisi",80),("wangwu",70))
scores: scala.collection.mutable.Map[String,Int] = Map(lisi -> 80, zhangsan -> 90, wangwu -> 70)
scala> val scores = mutable.Map(("zhangsan",90),("lisi",80),("wangwu",70))
<console>:12: error: not found: value mutable
val scores = mutable.Map(("zhangsan",90),("lisi",80),("wangwu",70))
3.3.3、访问映射中的值
根据键获取map中对应的值,可以有以下三种方法,推荐使用getOrElse方法。
//方式一
/如果key存在,则返回对应的值
//如果key不存在,则抛出异常[java.util.NoSuchElementException]
//在Java中,如果key不存在则返回null
scala> val score1 = scores("lisi")
score1: Int = 80
//方式二:使用getOrElse()取值
//def getOrElse[V1 >: V](key: K, default: => V1)
//如果key存在,返回key对应的值。
//如果key不存在,返回默认值。
scala> val score3 = scores.getOrElse("lisi",0)
score3: Int = 80
3.3.4 map遍历
package com.scala.day02
object MapDemo {
def main(args: Array[String]): Unit = {
val map=Map(("a",1),("b",2),"c"->3)
for (elem <- map.keys) {
println(elem,map(elem))
}
println("----------------------------")
for (elem <- map) {
println(elem)
}
println("上面遍历与下面遍历是一样的写法,本质就是迭代器")
for (elem <- map.iterator) {
println(elem)
}
println("----------------------------")
for ((k,v)<-map){
println(k,v)
}
println("----------------------------")
map.foreach((t:(String,Int))=>println(t._1,t._2))
println("此处需要注意一下" +
"不能写成如下" +
"map.foreach(t:(String,Int)=>println(“aaa”))" +
"与" +
"map.foreach((k:String,v:Int)=>println(k,v)" +
"注意格式,注意传递的参数的函数格式,是以map为一个整体的")
}
}
3.3.5 HashMap
3.3.6 TreeMap
3.2 列表 (List)
四、高阶函数
4.1 高阶函数的定义
定义:如果一个函数的传入参数为函数或者返回值是函数,则该函数即为高阶函数
一般高阶函数的类型有:
1、传入参数为函数
2、传入参数为匿名函数
3、传入参数为方法
4、传入参数为方法(方法自动转换成函数)
4.2常见的高阶函数
五、对象
5.1、单例对象
注意点:
1、在Scala中没有静态方法和静态字段,但是可以使用object关键字来达到同样的目的
2、单例对象虽然类似于Java中的工具类,但它不是类,是一个对象,可以把单例对象名看做一个贴在对象上的标签
3、类和单例对象的区别与联系
a
六、样例类(重点)
6.1、样例类介绍
1、“case class”一般被翻译成样例类,它是一种特殊的类,样例类是不可变的,可以通过值进行比较,可用于模式匹配。
2、样例类的定义方式:
case class 样例类名字(主构造器)
注:主构造器可加可不加,不加的话默认加
6.2、样例类特点
1、自动生成伴生关系
2、自动生成的伴生对象object提供apply方法(equals,unapply...),可以让我们不使用new关键字就能构造出相应的对象,并返回一个class实例
3、构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段),自动生成类的setter和getter构造器
6.3、样例类与普通类的区别
1、很显然,样例类有case关键字修饰,而普通类没有
2、样例类的一个优点是方便输出,反而普通类不方便,代码如下
3、样例类实现实例化不需要new关键字,而普通类需要
scala> case class Student(name:String,age:Int)
defined class Student
scala> println(Student("xiaofeng",18))
Student(xiaofeng,18)
scala> class Student1(name:String,age:Int)
defined class Student1
scala> println(new Student1("xiaoxiao",18))
$line81.$read$$iw$$iw$Student1@487d4820
6.4、样例类的实战
package com.scala.caseclass
/**
*样例类特点:
* 1、自动生成伴生关系
* 2、自动生成的object对象,提供apply方法(equals,unapply...),并返回一个class实例
* 3、构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段)
* 自动生成类的setter和getter构造器
*/
case class Student(name:String, age: Int)
//特点一: 构造器中的每一个参数默认为val,除非显示地声明为var,(也就是说不是自动变成对象私有字段)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val stu1 = new Student("jason",10)
// stu1.name="liq" //因为主构造器的参数默认为val 所以无法修改值,并不是对象私有字段
val stu2 = Student("jasss",18)
val stu3 = Student.apply("jasson",20)
println(stu2.name,stu2,args) //可以直接访问
// stu2.name = "xiaofeng" //修改值就不行 因为是val修饰的变量 相当于java中的常量
println("------------------")
println(Student("jason",10))
}
}
七、模式匹配
7.1、简单介绍
1、Scala是没有Java中的switch case语法的,但Scala提供了更加强大的match case语法,即模式匹配。
2、
Scala的match case与Java的switch case最大的不同点在于,Java的switch case仅能匹配变量的值,
而Scala的match case可以匹配各种情况,比如变量的类型、集合的元素、有值或无值等。
7.2、值匹配
//提示:其实不论常量匹配还是变量匹配,都是值匹配,自我感觉没啥区别,也没必要特意在意记住什么的
//常量
scala> val char = 'p'
char: Char = p
scala> val ch = char
ch: Char = p
scala> ch match{case 'p'=>println("1111"); case 'y'=>println("222");case _ =>println("333")}
1111
scala> ch match{case 'y'=>println("1111") case 'p'=>println("222") case _ =>println("333")}
222
//总结:其实加不加分号;都是一样的,只要执行了一个case,就不会执行其它的了,反而java中的switch case中需要break进行中断
//变量
scala> import Math.PI
import Math.PI
scala> val ch=Math.PI
ch: Double = 3.141592653589793
scala> ch match {case PI=>println(PI) case _=>println("others")}
3.141592653589793
scala> ch match {case x=>println(ch)}
3.141592653589793
scala> ch match {case y => println("this is "+ch) case PI=>println(PI)}
<console>:15: warning: patterns after a variable pattern cannot match (SLS 8.1.1)
ch match {case y => println("this is "+ch) case PI=>println(PI)}
^
<console>:15: warning: unreachable code due to variable pattern 'y' on line 15
ch match {case y => println("this is "+ch) case PI=>println(PI)}
^
<console>:15: warning: unreachable code
ch match {case y => println("this is "+ch) case PI=>println(PI)}
^
this is 3.141592653589793
scala> val ch = 'a'
ch: Char = a
scala> val y = 'b'
y: Char = b
scala> ch match {case PI=>println(PI); case y => println("this is "+ch)}
this is a
7.2.1 Scala对于常量还是变量模式的规则
1、以小写字母开头的名称为变量模式
2、如果常量符号也是小写字母开头,则需要使用反引号括起来:`i`
7.3、类型匹配
注意: 当你在匹配类型的时候,必须给出一个变量名,否则你将会拿对象本身来进行匹配
package com.scala.day04
import scala.util.Random
object TypeMatchDemo extends App {
//1、
// val arr = Array("this is string",2021,1.0,'a')
// var i= 0
// while(i<10){
// val obj = arr(Random.nextInt(4))
// obj match {
// case a:Int => println(a)
// case b:String => println(b.toUpperCase)
// case _:Double => println(Int.MaxValue)
// case _ =>println("0")
// }
// i+=1
// }
//2、
// class Person
// class Student(val name:String) extends Person
// val stu=new Student("xiaofeng")
//
// println(stu.getClass)
// println(stu.isInstanceOf[Person])
// println(stu.isInstanceOf[Student])
//
// stu match {
// case x:Person=>println("person")
// case x:Student =>println(x.name) //因为有第一个case,所以成为无法匹配的代码
// }
//3、其实下面两个 都可以
val lst=List[Any](1,2,3)
lst match {
case x:List[Int]=>println(x)
}
// val lst=List[Int](1,2,3)
//
// lst match {
// case x:List[Any]=>println(x)
// }
}
7.4、集合匹配
7.4.1、字符串匹配
/**
* 模式匹配-字符串
*/
object StringMatchDemo {
def main(args: Array[String]): Unit = {
val arr = Array("zhoudongyu", "yangzi", "guanxiaotong", "zhengshuang")
val name = arr(Random.nextInt(arr.length))
println(name)
name match {
case "zhoudongyu" => println("周冬雨")
case "yangzi" => println("杨紫")
case "guanxiaotong" => println("关晓彤")
case "zhengshuang" => println("郑爽")
case _ => println("Nothing ...")
}
}
}
7.4.2、数组匹配
代码1:
package com.scala.arraymatch
object ArrayMatchDemo extends App {
val arr1 = Array(0,0,0)
// println(Array(0))
println(arr1.isInstanceOf[Array[Int]])
val res = arr1 match {
case Array(0) => println(Array(0)(0))// == arr1(0) //匹配1个0的数组
case Array(x, y) => println(s"$x $y") // 匹配任何带有两个元素的数组,并将元素绑定到x和y
case Array(0, _*) => println("0...") //匹配任何以0开始的数组
case _ => println("something else")
}
println(res.getClass)
}
代码2:
package com.scala.arraymatch
object ArrayMatchDemo1 extends App {
val arr = Array(1,2,3,4)
arr match {
//匹配数组中的前两个元素
// case Array(x,y,_*)=>println(s"$x,$y") //输出 1,2
//匹配数组中的第三个元素
// case Array(_,_,x,_*)=>println(s"$x") //输入3
//匹配第一个元素
case Array(x,tail@_*)=>println(s"$x,$tail") //输出 1,Vector(2, 3, 4)
} // 任意字符串@都可以
}
7.4.3、列表匹配
package com.scala.listmatch
object ListMatchDemo extends App {
val list = List(1,2,3,4)
list match {
// case List(x,y,_*)=>println(s"$x,$y") //匹配数组中的前两个元素 输出 1,2
// case List(_,_,x,_*)=>println(s"$x") //匹配数组中的第三个元素 输出3
case List(x,zz@_*) =>println(s"$x,$zz") //输出1,List(2, 3, 4)
}
}
7.4.4、元组匹配
package com.scala.tuplematch
object TupleMatchDemo extends App {
val tuple = (1,2,3,4)
tuple match {
// case(x,y,_*)=>println(s"$x,$y") // 编译错误,元组不支持_*
case (x,y,z,_) =>println(x,y,z) //输出(1, 2, 3)
case _=>println("others")
}
}
7.5、样例类匹配
package com.scala.day04
/**
* 样例类匹配其实与普通类匹配一样的,只不过匹配需要 unapply方法
* 普通类匹配 需要unapply方法 才可以匹配,也就是说需要我们自己手动重写unapply方法
* 样例类匹配 则是自动提供了unapply方法,并且已经隐式的调用了
* 并且什么集合匹配 元组匹配 数组匹配 都隐式的调用了unapply方法
*
*/
object CaseClassMatchDemo extends App{
//1、普通类的模式匹配
val cat1 = new Cat1("xiaofeng",18)
//普通类的模式匹配
cat1 match {
case Cat1(name,id) => println(name,id)
}
//2、样例类的模式匹配
val cat = Cat("xiaoxiao",11)
cat match {
case Cat(name,id) => println(name,id)
}
}
//1、先看一下普通类匹配
class Cat1(val name:String,val id:Int)
object Cat1{
def unapply(arg: Cat1): Option[(String, Int)] = {
if (arg == null)
None //Some(("",0))
else
Some((arg.name,arg.id))
}
}
//2、样例类匹配
case class Cat(val name:String,val id:Int)
7.6、unapply提取器
unapply规则:
1、case 中对象的unapply方法返回Some集合则代表这个对象匹配成功。
2、反之如果返回为none集合则匹配失败
package com.scala.unapply
import scala.util.Random
class UnapplyDemo { }
object UnapplyDemo {
// apply的构造是返回一个name+数字的字符串
def apply(name:String):String = name+Random.nextLong()
def unapply(name:String): Option[String] ={
if(name.nonEmpty) Some(name) else None
}
def main(args: Array[String]): Unit = {
//创建一个对象,此时会触发apply方法
val c = UnapplyDemo("姜小白")
println(c)
//模式匹配 用来判断UnapplyDemo对象是否创建成功
/*
1、当将UnapplyDemo(name)写在case后时 就会默认触发伴生类中unapply方法
name 值会被传递到 def unapply(name:String)方法中就相当于将name赋值给了形参的name
如果判断对象是否创建成功 -->只要返回Some集合就认为创建成功后
开始执行 if判断语句 name.nonEmpty(不是空的) 返回some集合
若为空 返回None
case语句后来判断是否创建成功 */
c match {
case UnapplyDemo(name)=>println(name+"!!!")
case _=>println("没有")
}
}
}
7.7、 总结apply和unapply的区别
# apply
- apply方法通常会被称为注入方法(创建对象),在类的伴生对象中做以些初始化的操作
- apply方法的参数列表不需要和原生类的构造方法参数一致
# unapply
- unapply方法通常被称为提取方法,使用户unapply方法可以提取固定数量的对象或值
- unapply方法返回的是一个Option类型,因为unapply除了提取值之外,还有一个目的就是判断对象是否创建成功。若成功则有值,反之没有值,所有才使用Option做为返回值类型
ps:只要是object类,默认都是提供apply和unapply方法,是被默认隐式调用
7.8 、函数匹配(拓展)
package com.scala.functionmatch
/**
* 根据模式匹配写一下函数匹配
*/
object FunctionMatchDemo extends App {
val ff =(x:Int,y:Int)=>x+y //很常见
ff match {
case f:((Int,Int)=> Int)=> println(f(2,8))
}
val ff1:(Int,Int)=>Int = (x,y)=>x+y //也挺常见
ff1 match {
case f1:((Int,Int)=> Int)=> println(f1(2,8))
}
// val ff2 =(x,y)=>x+y //这么简写不行,在只有一个参数的时候可以这样写(只有一个参数也不能这样写,刚试验了)
//具体去查看一下函数的定义的那几种方式
val ff2 =(_:Int)+(_:Int)
ff2 match {
case f:((Int,Int)=> Int)=> println(f(2,8))
}
}
7.9、偏函数
7.9.1、说明
1、只对集合中的一部分数据进行处理的函数就是偏函数(scala.PartialFunction)
2、被包在花括号内没有match的一组case语句是一个偏函数(也就是说case表达式返回偏函数),它是PartialFunction[A, B]的一个实例,A代表输入参数类型,B代表返回类型,常用作输入模式匹配
3、PartialFunction是一个特质(详情请看源码),包括两个方法
a) isDefinedAt()方法: 对哪些数据进行处理
b) apply()方法:如何处理
7.9.2、代码如下
package com.scala.partialfuction
/**
* 如果使用偏函数对偶数进行处理
*/
object PartialFuctionDemo extends App {
val pf = new PartialFunction[Int,Double] {
override def isDefinedAt(x: Int): Boolean = {
x%2 == 0 //返回true 则表示定义对偶数进行处理
}
override def apply(v1: Int): Double = {
v1.toDouble
}
}
// println(pf(111))
List(1,2,3,4).collect(pf).foreach(println(_))
println(pf.applyOrElse(222,(x:Int)=>0d))
//总结:collect本质上也是调用applyOrElse方法,而applyOrElse则是先调用isDefinedAt 后调用apply
// 若调用applyOrElse 返回true 则调用apply
// 若调用applyOrElse 返回false 则不调用apply,不进行处理 返回默认的值
//case 表达式返回的也是一个偏函数
List(1,2,3,4).collect({case y:Int if y%2==0 => y.toDouble}).foreach(println(_))
}
被包括在花括号中的case表达式返回的是一个偏函数,实例如下:
scala> val pf = {case x:Int if x%2==0 =>x.toDouble}
<console>:11: error: missing parameter type for expanded function
The argument types of an anonymous function must be fully known. (SLS 8.5)
Expected type was: ?
val pf = {case x:Int if x%2==0 =>x.toDouble}
^
scala> val pf:PartialFunction[Int,Double] = {case x:Int if x%2==0 =>x.toDouble}
pf: PartialFunction[Int,Double] = <function1>
八、隐式转换
需要一个类型A,但是提供了另外的类型B,这是就需要隐式转换了,可以将B转换为A。
隐式转换对于名字不重要,重要的是类型
隐式转换发生的时间
1、先判断B是不是A类型,如果不是,就进行下一步
2、查找使用到的隐式转换类是否在当前的上下文定义,包括所有包、import导进来的对象等,查看是否存在【B->A】,如果存在,则会进行隐式转换,如果不存在,进行下一步
3、然后看是否能将当前调用的对象隐式地转换为目标对象,因为目标对象中拥有该方法。
例子如下:
1.to(10) Int中没有to方法,但是呢RichInt中有,Int就会隐式的转换为RichInt
#其它
1、如果需要的隐式参数未提供,则会查找当前的上下文是否有相应的隐式值
2、如果调用一个对象的不存在的方法,则会查找当前环境中是否有相应的隐式类扩展了这个对象所属类的方法
8.1、隐式值
1、隐式值=隐式的变量+隐式的参数
2、使用implicit修饰的参数就是隐式参数
3、隐式参数在调用的时候,可以给值,也可以不给值
4、不能出现多个相同类型的隐式变量,否则在使用的时候会出现因为不确定使用哪一个从而导致出错。但是可以有多个不同类型的隐式变量。
5、还有一个要注意的点,在调用隐式参数的时候,如果不给隐式参数赋值,那么会从当前上下文中查找隐式变量。
隐式参数的定义如下:
scala> def myfun(implicit x:Int)={x+1}
myfun: (implicit x: Int)Int
scala> myfun(1)
res1: Int = 2
//不提供隐式值时
scala> myfun
<console>:13: error: could not find implicit value for parameter x: Int
myfun
^
//解决办法
//和变量名无关,只要存在Int型的隐式值即可
scala> implicit val aaaaa = 2
aaaaa: Int = 2
scala> myfun
res3: Int = 3
//多个相同类型隐式值问题 只能有一个
scala> implicit val bbbbb = 3
bbbbb: Int = 3
scala> myfun
<console>:17: error: ambiguous implicit values:
both value aaaaa of type => Int
and value bbbbb of type => Int
match expected type Int
myfun
^
通过上面示例总结如下:
1、有隐式的值的方法,可以像普通方法一样去调用
2、在调用该方法的时候,可以不用传递隐式参数,而编译器会自动寻找一个implicit标记过的合适的值作为该参数
3、implicit 标记的值的类型 有且仅有一个才可以,若是有多个implicit标记的值的类型,就会出错
隐式值的应用如下:
scala> List(1,2,3,4).sort
sortBy sortWith sorted
scala> List(1,2,3,4).sortBy
def sortBy[B](f: Int => B)(implicit ord: scala.math.Ordering[B]): List[Int]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ORvcH5q5-1630938832075)(C:\Users\等待\AppData\Roaming\Typora\typora-user-images\image-20210607194021471.png)]
//通过查看源码
//方式一: 仿照Ordering[Int] 实现
scala> case class User(name:String,age:Int)
defined class User
scala> List(User("xiaofeng",18),User("xiaoxiao",20)).sortBy(user=>user)
<console>:18: error: No implicit Ordering defined for User. List(User("xiaofeng",18),User("xiaoxiao",20)).sortBy(user=>user) ^
scala> trait UserOrdering extends Ordering[User]{
| def compare(x:User,y:User) = if(x.age<y.age) -1 else 1
| }
defined trait UserOrdering
implicit object XXX extends UserOrdering
scala> List(User("xiaofeng",21),User("xiaoxiao",20)).sortBy(user=>user).foreach(println(_))
User(xiaoxiao,20)
User(xiaofeng,21)
//方式二:自行定义Ordering[User]的实例
package com.scala.implicitdemo
object ImplicitDemo extends App {
case class User(name:String,age:Int)
implicit val a = new Ordering[User]{
override def compare(x: User, y: User): Int = {
if (x.age<y.age) -1 else 1
}
}
List(User("xiaofeng",21),User("xiaoxiao",20)).sortBy(user=>user).foreach(println(_))
}
//结果如下:
//User(xiaoxiao,20)
//User(xiaofeng,21)
8.2、隐式方法
使用关键字implict修饰方法,就是隐式方法
一般情况下,是实现一个类型到另一个类型的隐式转换
问题:1.to(10) 对于Int并没有to方法,怎么能够进行调用的?还有其它很多方法,比如max()等
原因:经过查看源码,发现Int中没有此方法,但是它会隐式地转换成RichInt,RichInt中有此方法
本质:Int会隐式转换为RickInt
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-luIiRh9a-1630938832076)(C:\Users\等待\AppData\Roaming\Typora\typora-user-images\image-20210607201601088.png)]
应用:自定义一个MyInt,拥有max方法,只和0比较
//在IDEA中操作
package com.scala.implicitdemo
/**
*
* 下面例子 模仿 Int 转换为 RichInt
*
* 自定义一个MyInt,拥有max方法,只和0比较,然后 将Int转换为 MyInt,因为MyInt中有max0的方法
* 注意事项:
* 1、在一个上下文中,不能同时出现两个相同的类型转换方法
* 2、当隐式转换方法不在需要使用的方法中(方法中包含这个隐式方法)时,我们这个时候需要导入这个类
*
*/
//MyInt 类似 RichInt
class MyInt(x:Int) {
def max0() = {
if (x > 0) x else 0
}
}
object ImplicitDemo1 extends App {
implicit def int2MyInt(x:Int) = new MyInt(x)
// val a = 1
// a.max() //若是没有隐式方法,此处就会出错
println(10.max0()) //输出 10
}
//在scala的REPL中操作
class MyInt(x:Int){
def max()={ if(x>0) x else 0}
}
val a=1
a.max()
scala> a.max()
<console>:31: error: not enough arguments for method max: (that: Int)Int.
Unspecified value parameter that.
a.max()
//定义隐式转换函数
implicit def int2MyInt(x:Int):MyInt={new MyInt(x)}
a.max()
//在当前环境中能找到2个max方法,报错
scala> x.max()
<console>:32: error: type mismatch;
found : x.type (with underlying type Int)
required: ?{def max: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method intWrapper in class LowPriorityImplicits of type (x: Int)scala.runtime.RichInt
and method int2MyInt of type (x: Int)MyInt
//解决方式
MyInt中的max变为其他名
class MyInt(x:Int){
def max1()={ if(x>0) x else 0}
}
implicit def int2MyInt(x:Int):MyInt={new MyInt(x)}
8.3、隐式类(重点、经常用)
使用关键字implicit修饰的类就是隐式类,是对某一个已经存在的类的增强,是为了给某一个可扩展功能的。
在定义隐式类的时候,需要在主构造器中定义出需要增强的类类型的参数。
隐式类的作用:为现有的类增加新的方法。
//隐式类定义
implicit class MyClass(x:A){
def newMethod() //给类型A增加了新的功能
}
注意事项:
1、隐式转换类书写的位置要求:只能定义在class、trait、object中
2、隐式类中的主构造器只能有一个非隐式参数
3、隐式类不能是case class
4、使用的位置:如果用到的隐式转换类不在当前的上下文定义,需要导入包
实战:(给Int类型一个方法max0,完成和0的比较)
package com.scala.day05
object ImplicitDemo2 extends App {
implicit class MyInt(x:Int){
def max0() = {if (x>0) x else 0}
}
println(1.max0()) // 输出 1
implicit class MyStr(x:String){
def isPassword() = {
if (x == "123456") true else false
}
}
println("xiaofeng".isPassword()) //输出 false
println("123456".isPassword()) //输出true
}
九、泛型
注意:泛型参数是在运行时确定,在运行时会被替换,在编译期间无法检查边界问题。
9.1、相关概念
[B <: A] upper bounds 上限或上界:B类型的上界是A类型,也就是B类型的父类是A类型
[B >: A] lower bounds 下限或下界:B类型的下界是A类型,也就是B类型的子类是A类型
[B <% A] view bounds 视界:表示B类型要转换为A类型,需要一个隐式转换函数
[B : A] context bounds 上下文界定(用到了隐式参数的语法):需要一个隐式转换的值
[-A]:逆变,作为参数类型。是指实现的参数类型是接口定义的参数类型的父类
[+B]:协变,作为返回类型。是指返回类型是接口定义返回类型的子类
泛型类的定义:
//带有类型参数A的类定义
class Stack[A] {
private var elements: List[A] = Nil
//泛型方法
def push(x: A) { elements = x :: elements }
def peek: A = elements.head
def pop(): A = {
val currentTop = peek
elements = elements.tail
currentTop
}
}
使用上述的泛型类,使用具体的类型代替类型参数A
val stack = new Stack[Int]
stack.push(1)
stack.push(2)
println(stack.pop) // prints 2
println(stack.pop) // prints 1
9.2、上界
上界定义: T <: A
,表示类型变量T
必须是 类型A
子类(<=A)
泛型注意点看MyClass,上界看MyClass1
package com.scala.generic
/**
* 泛型和上界
*/
object GenericDemo {
def main(args: Array[String]): Unit = {
// val obj = new MyClass //若是不给具体的类型,会是nothing类型
val obj = new MyClass[Int]
obj.x = 1
obj.printt
println("---------------------------")
// val obj1 = new MyClass1[String] //报错,因为String不是AnyVal的子类
// obj1.x = "xiaoxiao" // 应该 <= AnyVal
// obj1.printt
val obj1 = new MyClass1[AnyVal]
obj1.x = true //AnyVal类型的子类或AnyVal都行
obj1.printt
}
}
//使用了泛型,不建议 使用具体的方法,怎么说呢,比如下面的例子
//这自定义的泛型例子 其实就是说明 只能使用其他具体的类型
class MyClass[A]{
var x:A = _
def printt = {
// x+1 //是错误的,只能加字符串,因为还不知道A具体的类型,不同于常规的类型
println(x + " xiaofeng") //具体 可以看一下源码
if (x.isInstanceOf[Int])
println(x.asInstanceOf[Int] + 1)
else
println("other type")
}
}
class MyClass1[A<:AnyVal]{
var x:A = _
def printt = {
println(x + " xiaoxiao")
if (x.isInstanceOf[Int])
println(x.asInstanceOf[Int] + 1)
else
println("other type")
}
}
9.3、下界
语法 B >: A
表示参数类型或抽象类型 B
是类型A的父类。通常,A是类的类型参数,B是方法的类型参数。
/**
* 下界,所以应该是,传入Person和Man
**/
abstract class Person {
def name:String
}
class Man extends Person{
override def name: String = "Man"
}
class oldMan extends Man{
override def name: String = "oldMan"
}
//创建一个类,使用下界限定传入的参数
// >: 下界 [B >: A] B类型的子类是A类型
class Man2[M>: Man](m:M){
def Man:M = m
def show[M >: Man]()=println("")
}
object Demo_3{
def main(args: Array[String]): Unit = {
// Scala是在运行报错,编译阶段是不会报错
//下界,所以应该是,传入Person和Man
val man = new Man2[Man](new Man)
//是有问题的 oldMan即不是Man的父类 也不是Man类型
//val om = new Man2[oldMan](new oldMan)
}
}
9.4、视界(view bounds)
注意:已过时,了解即可
视界定义: A <% B
,表示类型变量A 必须是 类型
B的子类,或者A能够隐式转换到B
package com.scala.day05
/**
* 上界和视界(了解即可,用的不多)
*/
object GenericDemo1 extends App {
// class GenericComparator[T<:Comparable[T]](x:T,y:T){
// def getbigger = {
// if (x.compareTo(y)>0) x else y
// }
// }
class GenericComparator[T<%Comparable[T]](x:T,y:T){
def getbigger = {
if (x.compareTo(y)>0) x else y
}
}
//情况一、 以String类型为例子
val aa = new GenericComparator[String]("a","c") //String本身就是Comparable,因为它实现了Comparable接口,查看源码就知道了
println(aa.getbigger)
//情况二、以Int类型为例子
/**
* Scala语言里 Int类型没有实现Comparable;
* 那么该如何解决这个问题?
* 在scala里 RichInt实现了Comparable, 如果我们把Int转换为RichInt类型就可以这样实例化了.
* 在scala里 <% 就起这个作用, 需要修改Pair里的 <: 为<% 把T类型隐身转换为Comparable[Int]
* String可以被转换为RichString. 而RichString是Ordered[String] 的子类.
*/
val bb = new GenericComparator[Int](1,2) //Int本身不是Comparable ,Int会自动转换为RichInt
println(bb.getbigger) // 所以这个上界是不对的 应该使用视界,会自动提供一个隐式转换函数 成Int
//情况三 、 自定义的类 为例子
case class Student(name:String,age:Int)
//需要提供普通类student隐式转换为Comparable[Student]
implicit def student2comparableStudent(student: Student):Comparable[Student]={
new Comparable[Student] {
override def compareTo(o: Student): Int = {
student.age-o.age
}
}
}
val cc = new GenericComparator[Student](Student("xiaofeng",18),Student("xiaoxiao",20))
println(cc.getbigger)
}
9.5、上下文界定(context bounds)
上下文界定的形式为 T : M, 其中M 必须为泛型类, 必须存在一个M[T]的隐式值
package com.scala.day05
//必须存在Ordering[T]这样的隐式值才可以
class Pair_Context[T : Ordering](val first: T, val second: T){
def smaller(implicit ord: Ordering[T]) =
if(ord.compare(first, second) < 0) first else second
}
object GenericDemo3 {
def main(args: Array[String]): Unit = {
val pair = new Pair_Context("Spark", "Hadoop")
println(pair.smaller)
val int = new Pair_Context(3, 5)
println(int.smaller)
case class User(name:String,age:Int)
implicit val a=new Ordering[User]{
override def compare(x: User, y: User): Int = x.age-y.age
}
val user=new Pair_Context[User](User("Jason",20),User("ammy",21))
println(user.smaller)
println(user.smaller(new Ordering[User]{
override def compare(x: User, y: User): Int = y.age-x.age
}))
}
}
9.6、泛型难点小结
# 视界:view bounds 格式 [T% Comparable[T]] 要求T可以隐式转换为Comparable[T] 一般通过隐式函数实现
implicit def user2comparableUser(user: User):Comparable[User]
# 上下文界定 :context bounds 格式 [T:Ordering] 要求存在Ordering[T]的隐式值 一般通过隐式值实现
implicit val a=new Ordering[User]{
override def compare(x: User, y: User): Int = x.age-y.age
}
9.7、不变
不变、协变和逆变 一般都用在函数当做参数上
不变 :A是B的父类,Array[A]和Array[B] 没关系
def m1(a:Array[Any])=println(a)
m1(Array(1,2,1))
m1(Array("a"))
9.8、协变
不变、协变和逆变 一般都用在函数当做参数上
协变+ :A是B的父类,List[A]是List[B]的父类
def m1(a:List[Any])=println(a)
scala> m1(List(1,2,3))
List(1, 2, 3)
scala> m1(List("aa"))
List(aa)
9.9、逆变(重点)
不变、协变和逆变 一般都用在函数当做参数上,一般函数当做函数时是逆变
逆变-:A是B的父类,Function1[A]是Function1[B]的子类 (掌握)
def m1(f:Int=>Int)=println(f(1))
m1((x:Int)=>x+1) //正常
m1((x:Double)=>x.asInstanceOf[Int]+1) //错误
m1((x:AnyVal)=>x.asInstanceOf[Int]+1) //正常
m1((x:Nothing)=>x.asInstanceOf[Int]+1) //错误
trait Function1[-T1, +R]
由是Function的参数 逆变的,所有Any=>Int 返回的函数类型是Int=>Int的子类
高阶函数中的参数可选范围往上找 比如Int为入参,可以接收AnyVal或者Any
逆变的测试且看文档
十、多线程
十一、正则表达式
ser=new Pair_ContextUser
println(user.smaller)
println(user.smaller(new Ordering[User]{
override def compare(x: User, y: User): Int = y.age-x.age
}))
}
}
### 9.6、泛型难点小结
~~~markdown
# 视界:view bounds 格式 [T% Comparable[T]] 要求T可以隐式转换为Comparable[T] 一般通过隐式函数实现
implicit def user2comparableUser(user: User):Comparable[User]
# 上下文界定 :context bounds 格式 [T:Ordering] 要求存在Ordering[T]的隐式值 一般通过隐式值实现
implicit val a=new Ordering[User]{
override def compare(x: User, y: User): Int = x.age-y.age
}
9.7、不变
不变、协变和逆变 一般都用在函数当做参数上
不变 :A是B的父类,Array[A]和Array[B] 没关系
def m1(a:Array[Any])=println(a)
m1(Array(1,2,1))
m1(Array("a"))
9.8、协变
不变、协变和逆变 一般都用在函数当做参数上
协变+ :A是B的父类,List[A]是List[B]的父类
def m1(a:List[Any])=println(a)
scala> m1(List(1,2,3))
List(1, 2, 3)
scala> m1(List("aa"))
List(aa)
9.9、逆变(重点)
不变、协变和逆变 一般都用在函数当做参数上,一般函数当做函数时是逆变
逆变-:A是B的父类,Function1[A]是Function1[B]的子类 (掌握)
def m1(f:Int=>Int)=println(f(1))
m1((x:Int)=>x+1) //正常
m1((x:Double)=>x.asInstanceOf[Int]+1) //错误
m1((x:AnyVal)=>x.asInstanceOf[Int]+1) //正常
m1((x:Nothing)=>x.asInstanceOf[Int]+1) //错误
trait Function1[-T1, +R]
由是Function的参数 逆变的,所有Any=>Int 返回的函数类型是Int=>Int的子类
高阶函数中的参数可选范围往上找 比如Int为入参,可以接收AnyVal或者Any
逆变的测试且看文档