一、特质
1.特质更像是抽象类,有抽象的方法也可以有具体方法,抽象字段和具体字段,不能有类参数,有不同的地方是,super是动态绑定的,即写super.进行方法调用时没办法判定绑定的是哪个特质或类的方法,只有在混入具体的类时(scala可以多继承)才能判定。还有特质的定义使用trait关键字。
使用特质可以用extends或者with关键字混入类或者抽象类中。
trait A {
def speak() = println("I am balabala!")
}
//B继承A特质
class B extends A{}
class C {}
//如果类有显式的父类,混入A特质时则需要使用with关键字
//可以使用多个with关键字混入多个特质,看起来会因为多重继承引起方法调用不明确的问题
//这个稍后会解释在scala中如何处理
trait B {
def speak() = println("I am balabala!")
}
class D extends C with A with B
2.Order特质
两个同一类型的对象的比较时,只需要在类实现时继承Ordered特质并实现compare方法就可以实现对象的>、<、>=和<=的比较。例如:
class Test(val a:Int) extends Ordered[Test]{
def compare(that:Test){
this.a - that.a
}
}
val a = new Test(1)
val b = new Test(2)
println(a > b)
println(a >= b)
println(a < b)
println(a <= b)
3.特质堆叠
我的理解来说,特质的堆叠解释了一个多重继承的问题,scala中的解决方法是线性化。依然还是举书上的栗子吧:
假设实现一个抽象的整数队列,有两种操作,一种是put,把整数放入队列,另外一种是get,从队列尾部取出一个整数。
abstract class IntQueue{
def get():Int
def put(x:Int)
}
//一个实现类:
class BasicIntQueue extends IntQueue{
private val buf = new ArrayBuffer[Int]
def get() = buf.remove(0)
def put(x:Int) {buf += x}
}
定义了三个特质如下:
//1.特质继承自IntQueue,则特质只能混入IntQueue及其子类中
//2.特质中的super调用是线性化调用,只有在混入类或者特质的时候才会有意义
//3.abstract override标识符仅在物质成员定义中可以使用,
//意味着特质必须被混入具有期待方法的具体的类中。
//将放入队列的数字加倍
trait Doubling extends IntQueue{
abstract override def put(x:Int) {super.put(2 * x)}
}
//将放入队列的数字加1
trait Incrementing extends IntQueue{
abstract override def put(x:Int) {super.put(x + 1)}
}
//过滤放入队列的数字,如果为负数则不放入队列。
trait Filtering extends IntQueue{
abstract override def put(x:Int) {if(x >= 0) super.put(x)}
}
//这个是普通的使用,没有多继承的问题
val queue = new BasicIntQueue with Doubling
queue.put(10)
queue.get()//20
//以下这个类混入两个特质,put方法调用的是哪个呢,答案是put的调用顺序从右向左依次
//调用,先调用Filtering的然后是Incrementing的,所以结果是第一个put的值会先被
//过滤掉,而第二和第三个则不会被过滤,然后才会调用Incrementing的put方法加1,
//最后调用BasicIntQueue的put方法存入队列。
val myQueue = new BasicIntQueue with Incrementing with Filtering
myQueue.put(-1)
myQueue.put(0)
myQueue.put(1)
myQueue.get()//1
myQueue.get()//2
//也可以调整一下特质混入的顺序如下,则会先调用Incrementing的put方法加1,
//然后才调用Filtering的put方法过滤负数,所以会产生如下结果:
val otherQueue = new BasicIntQueue with Filtering with Incrementing
otherQueue.put(-1)
otherQueue.put(0)
otherQueue.put(1)
otherQueue.get()//0
otherQueue.get()//1
otherQueue.get()//2
4.线性化
线性化,当使用new实例化一个类的时候,scala把这个类以及所有它继承的类还有它混入的特质以线性的次序放在一起。当其中某个类调用super的时候指的就是这个线性次序的下一个环节。
二、包和引用
1.普通写法的和Java中一致,如package com.aaa.bbb
另外一种写法则可以将同一文件中的不同类放到不同包中,比如:
package com{
package aaa{
//属于包com.aaa
class App{}
package bbb{
//属于包com.aaa.bbb
class TestApp{}
}
}
}
//还有另一种简写的方式
package com.aaa{
class App{}
package bbb{
class TestApp{}
}
}
2.scala中包的简单写法
package com{
package aaa{
class App{}
}
package bbb{
//这里的引用可以不写绝对路径
//另外scala中提供了一个—_root_包,它是所有用户创建的包的顶层包
//即下面的包也可以写成_root_.com.aaa.App,只是为了举例,这个顶层
//包的作用是为了子包和上层的包名有冲突时使用的
val test = new aaa.App
}
}
3.包引用
引入的方式
//引入com.aaa.bbb包
import com.aaa.bbb
//引入com.aaa包下的所有成员
import com.aaa._
//引入类APP下的所有成员
import com.aaa.APP._
//引入aaa包下的AA和BB成员
import com.aaa.{AA,BB}
//引入aaa包下的AA和BB成员,并把AAA重命名为A,可以使用A代替AAA
import com.aaa.{AAA => A,BB}
//引入aaa包下的所有成员,并把AAA重命名为A,可以使用A代替AAA
import com.aaa.{AAA => A,_}
//引入aaa包下的除AAA外的所有成员,
import com.aaa.{AAA => _,_}
scala中的包引用可以放到任何地方,比如可以放到一个方法中:
def test(p:Person){
//如下引用则可以直接使用Person类中的成员
import p._
println(name+"--"+age)
}
每一个scala类中都默认引用了三个包,java.lang.,scala.,Predef._分别是java和scala的标准库还有scala中的常用的工具类。如果有相同成员,后引用的将会覆盖先引用的,比如:StringBuilder在java.lang和scala中都有,则默认会引用scala中的。
4.访问修饰符
private与Java中类似只能在类或者对象的内部访问,并且规则也应用到内部类上,比如:
class Outer{
class Inner{
private def f() {println("fun")}
class InnerMost{
//可以调用
f()
}
}
//不可以,因为不在Inner类内部
(new Inner).f()
}
protected也相对比Java中的限制要多一些,比如:
package p{
class Super{X
protected def fun() {println("fun")}
}
class Sub extends Super{
fun()
}
class Other{
//不可以,因为不是Super的子类
fun()
}
}
public成员没有显示的标识符,只要没有明确标识Private或者protected的成员都是public,在任何地方都可以访问
作用域
scala中有更为细颗粒度的控制,比如private[X]或者protected[X],X指的可以是包、类或者单例对象,示例:
package bobsrockets{
package navigation{
private[bobsrockets] class Navigator{
//在navigation包和Navigator类及之类中可见
protected[navigation] def userStarChart(){}
class LegOfJourney{
//在类Navigator中可见
private[Navigator] val distance = 100
}
//只在Navigator的同一个实例中可见
private[this] var speed = 200
}
}
package lanuch{
import navigation._
object Vehicle{
//lanuch包在bobsrockets中,根据private[Navigator]这个定义,则可以访问Navigator类
private[lanuch] val guide = new Navigator
}
}
}