Scala教程(十六)Scala复合类型与依赖注入详解
1 Scala类型与依赖注入
1.1 函数链式调用
Scala中任务对象都有type属性,cat对象调eat方法时,this指向当前调用对象。
class Animal { def breathe : this.type = this }
class Cat extends Animal { def eat(){println("=====eat=====")}}
object SingletonTypes {
def main(args: Array[String]): Unit = {
val cat = new Cat();
// 执行结果:=====eat=====
cat.breathe.eat();
}
}<span lang="EN-US" style="font-family:Consolas;color:black;font-size:12.0pt;">
</span>
1.2 路径依赖
Scala的内部类比Java的内部类具有更多特性,因此Scala引入了路径依赖类型(Path-dependent type)的概念。像outer.Inner这样的类型就称为路径依赖类型。所谓路径,指的就是参考外部类所建立的实例的名称。 就outer.Inner这个类型来说,路径为outer。更重要的是,不同路径代表不同的类型。
val o1 = new Outer;
val o2 = new Outer;
val i1 = new o1.Inner;
val i2 = new o2.Inner;
// o1.Inner类属于Outer#Inner的子类,这方式称之为类型投影
val i: Outer#Inner = new o1.Inner
尽管o2和o1引用同一个实例,但o1.Inner和o2.Inner是不同的,Scala编译器只根据路径名进行区分:
val o1 = new Outer
val o2 = o1
val i1: o1.Inner = new o1.Inner
val i2: o2.Inner = new o1.Inner // 编译错误。
1.3 结构类型
结构类型又称之为鸭子类型,即:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
// 伴生类
class StructuralType{
// 定义函数
def open() = println("A class instance Opened");
}
object StructuralType {
def main(args: Array[String]): Unit = {
// new {} 对象,对象中存在 open方法,即可调用open。执行结果:Opende
init(new {def open() = println("Opende")});
// type:即把 = 后面的内容命名为别名
type x = {def open():Unit}
object A {def open() {println("A single object Opened")}}
// 执行结果:A single object Opened
init(A);
// structural 实例中包含open方法,即可调用open。执行结果:A class instance Opened
val structural = new StructuralType();
init(structural);
}
// 结构类型又称之为鸭子类型,即:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
// 定义init函数,参数具有open方法的对象
def init(res:{def open():Unit}){
res.open();
}
/*
* 不过Scala编译成Reflection实现:Why scala uses reflection to call method on structural type?
* 文章中说性能差事反射的性能问题,随着JVM引入invokedynamic 指令,会有很大的性能提升,但是这只在JVM1.7才有,
* 但是scala2.11还支持JVM1.6, 所以最好还是不要大量使用,当然目前貌似也没有太多使用的。
*
* Reflection参考文章:
* 1. http://en.wikibooks.org/wiki/Scala/Structural_Typing
* 2. http://java.dzone.com/articles/duck-typing-scala-structural
* 3. http://www.draconianoverlord.com/2011/10/04/why-no-one-uses-scala-structural-typing.html
* 4. http://stackoverflow.com/questions/8539422/why-scala-uses-reflection-to-call-method-on-structural-type
*/
}
1.4 复合类型
T1with T2 with T3 …,这种形式的类型称为复合类型(compoundtype)或者也叫交集类型(intersection type)。
// 定义特征(CompoundType1、CompoundType2)
trait CompoundType1;
trait CompoundType2;
class CompoundType extends CompoundType1 with CompoundType2;
object CompoundType {
// 定义方法compoundType,该方法需要传递即参数满足CompoundType1类型且又要满CompoundType2类型的,复合类型。
def compoundType(x:CompoundType1 with CompoundType2) = {println("Compound Type in global method!!!")}
def main(args: Array[String]): Unit = {
// new出即符合CompoundType1类切也符合CompoundType2类型,执行结果:Compound Type in global method!!!
compoundType(new CompoundType1 with CompoundType2)
// object 混入代码中,将object对象当参数传递方法,执行结果:Compound Type in global method!!!
object compoundTypeObject extends CompoundType1 with CompoundType2
compoundType(compoundTypeObject);
// 使用type关键字,起别名
type compoundTypeAlias = CompoundType1 with CompoundType2;
// 定义函数,参数使用别名compoundTypeAlias类型
def compoundTypeLocal(x:compoundTypeAlias) = println("Compound Type in local method!!!");
// 别名的函数调用,执行结果:Compound Type in local method!!!
var compoundTypeClass = new CompoundType();
compoundTypeLocal(compoundTypeClass);
// 定义复合类型:在CompoundType1并且CompoundType2类,同时必须实现在init方法。
type Scala = CompoundType1 with CompoundType2 {def init():Unit}
}
}
1.5 scala中缀表达
scala中的中缀表达在3个地方存在:操作符、模式匹配、类型声明。中缀操作符最常见: a +b 或 x foo y 里的+和foo都是中缀操作符(严格的说scala里没有操作符,其实都是方法)。
object InfixType {
def main(args: Array[String]): Unit = {
// >>:操作符是函数名,定义def。 把数据追加到log中,执行结果:Scala Spark Hadoop
object Log { def >>:(data: String): Log.type = { print(data + " "); Log } }
"Hadoop" >>: "Spark" >>: "Scala" >>: Log
// 最常见的从右往左结合的中缀操作符是 List 的 :: 方法
val list = List();
val newList = "A" :: "B" :: list;
// 执行结果: List(A, B)
println(newList);
// 中缀类型:泛型带有两个带参数的类型A,B。
// 语法:类型 ClassName 类型:即 Int InfixType String 等同于 InfixType[Int,String]
class InfixType[A, B]
val infix: Int InfixType String = null;
// 模式匹配:中缀表达
case class Cons(first: String, second: String)
val case_class = Cons("one", "two");
case_class match {
// 即 "one" Cons "two" 等同于 Cons("one", "two")
case "one" Cons "two" => println("Spark!!!");
}
}
}
1.6 scala自身类型
/*
* self不是关键字,可以用除了this外的任何名字命名(除关键字)。
* this代表当前对象,this和self是等价的。
*/
class Self {
self => // this别名
val tmp = "Scala";
def foo = self.tmp + this.tmp
}
trait S1
// S2在实例化时或定义S2的子类时,必须混入指定的S1类型(也可以为当前类型,如:class S2 {this:S2 => })
class S2 { this: S1 => }
class S3 extends S2 with S1
// class S4 { this => } //error 不能用this做别名
object SelfType {
def main(args: Array[String]): Unit = {
/*
* 内部类
*/
class Outer {
outer => // Outer类的this别名outer
val v1 = "Spark";
class Inner {
// 用outer表示外部类,相当于Outer.this
println(outer.v1);
}
}
// 构造对象,实例化S2必须混入S1
var c = new S2 with S1
}
}
1.7 scala抽象类型
trait Reader {
// 定义类型In,In类型的上界为java.io.Serializable
type In <: java.io.Serializable;
type Contents;
// 定义read函数,参数为In类型,返回值为Contents类型
def read(in: In): Contents;
}
/*
* FileReader类实现Reader特征
*/
class FileReader extends Reader {
// 在实现类中In类型被赋值具体String类型
type In = String;
// Contents类型被赋值具体BufferedSource类型
type Contents = BufferedSource;
// 实现read函数
override def read(name: String) = Source.fromFile(name);
}
object AbstractTypes {
def main(args: Array[String]): Unit = {
val fileReader = new FileReader();
val file = fileReader.read("E:\\input.txt")
/*
* 执行结果:
* I feel Great!!!
* I am into Music so much!!!
*/
for (line <- file.getLines()) { println(line); }
// 文件关闭
file.close();
}
}
1.8 AOP依赖注入
依赖注入:是指依赖对象的创建,由第三方完成,而不是被依赖对象,我们将这种控制关系的转移,称为依赖注入或者控制反转。
trait Logger {
def log(msg: String)
}
trait Auth {
auth: Logger => // 别名auth
def act(msg: String) {
// 调用被依赖的对象Logger 的log 方法
auth.log(msg)
}
}
object DI extends Auth with Logger {
// 重写父类的方法
override def log(msg: String) = println(msg);
}
object DependencyInjection {
def main(args: Array[String]) {
// DI为静态对象,执行结果:dependency injection
DI.log("dependency injection")
}
}
--以上为Scala复合类型与依赖注入详解,谢谢大家对我的关注。
——厚积薄发(yuanxw)