记录一些scala中的重要概念
T <: 上界 类型变量界定
T >: 下界 类型变量界定
T <& 存在隐式函数、隐式值 对类型进行视图界定
T >& 存在隐式函数、隐式值 对类型进行视图界定
T : Ordering 上下文界定 T属于ordering类型
classtag 泛型运用反射,编译时虚拟机并不能识别泛型类型,予以擦出,程序运行时,动态获取类型
并把类型赋予给classtag,用以得到具体类型。
协变,B是A的子类,如果list[B]是list[A]的子类则是协变,反之是逆变。
访问权限,默认是public类型,如果是private类型,伴生对象和伴生类之间可以相互访问,
否则不能访问.可以在object中apply方法里new一个类的对象实现,直接调用object就可以
new出对象。如果是private[this]类型,则只能当前对象进行调用[this对象],其他类中
new一个private[this]类型的对象调用该变量也不能调用该变量。
内部类在java和scala中的区别:在java中,内部类需要先new出外部类,然后用.new内部类,内部类的
类型是一样的,直白的说就是同一个对象地址,而scala中的内部类,通过new 外部类.内部类的方式,
对象不是同一个对象,地址不同。
偏函数:直白的说就是用参数调用一个方法,然后再往方法里传参数,返回值。
闭包:定义了一个变量,这个变量,离开了它的作用域,依然可以使用它。
最后说说比较重要的,隐式转换:直白的说,就是本来一个类没有这个方法,但是通过隐式转换,它拥有了这个方法。
隐式转换的时机:
1.当方法中的参数的类型与目标类型不一致时。
2.当对象调用类中不存在的方法或成员时,编译器会自动将对象进行隐式转换。
隐式解析机制
即编译器是如何查找到缺失信息的,解析具有以下两种规则:
1.首先会在当前代码作用域下查找隐式实体(隐式方法 隐式类 隐式对象)。
2.如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。
类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下:
(1)如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析过程中,它们的伴生对象都会被搜索。
(2)如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分,比如List[String]的隐式搜索会搜索List的伴生对象和String的伴生对象。
(3)如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索。
(4)如果T是个类型注入S#T,那么S和T都会被搜索。
隐式转换的前提:
1.不存在二义性。
2.隐式操作不能嵌套使用。
隐式参数使用的常见问题:
(1)当函数没有柯里化时,implicit关键字会作用于函数列表中的的所有参数。
(2)隐式参数使用时要么全部不指定,要么全不指定,不能只指定部分。
(3)同类型的隐式值只能在作用域内出现一次,即不能在同一个作用域中定义多个相同类型的隐式值。
(4)在指定隐式参数时,implicit 关键字只能出现在参数开头。
(5)如果想要实现参数的部分隐式参数,只能使用函数的柯里化,
如要实现这种形式的函数,def test(x:Int, implicit y: Double)的形式,必须使用柯里化实现:def test(x: Int)(implicit y: Double)。
(6) 柯里化的函数, implicit 关键字只能作用于最后一个参数。否则,不合法。
(7)implicit 关键字在隐式参数中只能出现一次,柯里化的函数也不例外。
(8)匿名函数不能使用隐式参数。
(9)柯里化的函数如果有隐式参数,则不能使用其偏应用函数。
(10)单一调用规则:隐式转换只会匹配一次,即隐式转换至多发生一次不会叠加(重复嵌套)使用隐式转换。一次隐式转化调用成功之后,编译器不会再去寻找其他的隐式转换。
(11)显示操作优先规则:当前代码类型检查没有问题,编译器不会尝试查找隐式转换。
(12)所有隐式转换必须用implicit关键字修饰。
隐式转换
import scala.io.Source
import java.io.File
//这里的RichFile相当于File的增强类 需要将被增强的类作为参数传入构造器中
class RichFile(val file: File) {
def read = {
Source.fromFile(file.getPath).mkString
}
}
//implicit是隐式转换的关键字 这里定义一个隐式转换函数把当前类型转换成增强的类型
object Test {
//File --> RichFile
implicit def file2RichFile(file: File) = new RichFile(file)
}
object App {
def main(args: Array[String]): Unit = {
//导入隐式转换
import Test.file2RichFile
//File类本身没有read方法 通过隐式转换完成
//这里的read方法是RichFile类中的方法 需要通过隐式转换File --> RichFile
println(new File("D:\\").read)
}
}
隐式值的应用
object Implicits {
implicit val default: String = "Spark"
}
object Param {
//函数中用implicit关键字 定义隐式参数
def print(context: String)(implicit skill: String){
println(skill+":"+context)
}
}
object Implicit_Parameters {
def main(args: Array[String]): Unit = {
//隐式参数正常是可以传值的,和普通函数传值一样,但是也可以不传值,因为有缺省值(默认配置)
Param.print("Spark")("MLlib")
import Implicits._
//隐式参数没有传值,编译器会在全局范围内搜索 有没有implicit String类型的隐式值,并传入
Param.print("Hadoop")
}
}
通过伴生对象隐式转换
import java.io.File
import scala.io.Source
class RichFile(val file: File) {
def read = Source.fromFile(file.getPath).mkString
}
class Test(path: String) extends File(path)
object Test {
implicit def file2RichFile(file: File) = new RichFile(file) //file-->RichFile
}
object Implicits_Internals {
def main(args: Array[String]): Unit = {
/*
* 这里没有导入隐式对象
*
* 通过给Test类 构建一个伴生对象 在伴生对象内部顶一个隐式转换的方法
*
* 执行顺序:
* 1.搜索Test有无read方法
* 2.在上下文上搜索(有无导入的隐式对象)
* 3.搜索Test的伴生对象内有无隐式转换 发现implicit关键 尝试匹配类型
* 例如这里匹配file2RichFile(file: File) 返回类型为RichFile 在RichFile中发现read方法
*/
println(new Test("D:\\").read)
}
}