标签(空格分隔): scalajava
属性的声明:
scala类的属性有4种方法定义:var
, val
, private var
, 同类对象私有字段private[this] var
python
class Person{ var varage = 0 // 类中全部var产生公有的setter和getter val valage = -1 // 类中全部val产生公有的getter private var age1 = 0 // 产生私有的setter和getter private[this] var age2 = 3 // 不产生setter,getter // 经过复写setter和getter def age = age2 // getter def age_=(newAge:Int) = { // setter : field_= this.age2 = newAge } }
scala的setter和getter调用es6
val p = new Person p.varage_=(2) // setter p.age // getter
主副构造器
(1)若是一个类没有显示的声明主构造器,则会自动加入无参构造
(2)辅助构造器名称为this(避免修改类名时要修改多个辅助构造器的名称)'
(3)辅助构造器的开头必须以主构造器开始
(4)主构造器中的字段会自动被解析成类中的属性api
class Person(var name:String,var age:Int){ def this(name:String){ this(name,-1) } def update(name:String,age:Int) = { print("update function is called") this.age = age } }
伴生对象
(1)伴生对象适用于既有实例方法,又有静态方法的时候
(2)伴生对象中的apply方法能够用来不带new产生对象,apply的方法体要调用伴生类的主/辅助构造器方法
(3)伴生对象中的unapply方法,能够在模式匹配中用于属性匹配
```scala
object Person{
def apply(name: String): Person = new Person(name)
def unapply(arg: Person): Option[Int] = Option(arg.age)
}缓存
// 继承的写法
class Student(name:String,age:Int,val sid:String) extends Person(name){
}网络
object Main extends App{
val p = Person("lj") // 利用伴生对象的apply方法产生对象
p match { // 模式匹配至关于手动调用了下面的unapply方法
case Person(-1) => println("match success")
}
if (Person.unapply(p).get == -1)
println("unapply match success")app
val s1 = new Student("lj",26,"09101306")ide
p("lj") = 27 // update function
}
```函数
声明枚举类型
(1)object继承Enumeration
(2)枚举的属性调用Value方法
(3)枚举的name自动设置为属性名
```scala
object Color extends Enumeration{
val Red = Value
val Yellow = Value
}this
object Test1 extends App{
println(Color.Yellow.toString) // Yellow
println(Color.Yellow.id) // 1
}
```
def getOptval(aaa:Person):Option[Person] = Some(aaa) print(getOptval(new Person(1,2)).get) //Person@7921b0a2
(1)全部集合继承自Iterable特质,所以,访问全部集合的通用代码为:
val coll = ... // 某种集合 val iter = coll.iterator while(iter.hasnext) iter.next
(2)scala的集合大体分为3类:
i) Seq:按照插入顺序排序的序列
ii) Set:每次插入一个元素,都会根据某种经排序方法决定元素在集合中所处的位置。set中没有重复的元素
iii) Map:键值对对偶
不可变序列:
(1)Vector是ArrayBuffer的不可变版本,它拥有下标,以树型结构存储节点,支持快速的随机访问。每一个节点最多可存放32个子节点。所以,对于一个100万个元素的向量,只须要四层节点(\(10^6 \approx32^4\)),访问任意一个元素,最多只须要4眺
(2)Range是一个整数序列,好比1,2,3,4,5,6,7,8,9 它不存储全部元素,只存储起始值,结束值和增值
可变序列:
scala中的列表要么是Nil(空表),要么是一个head元素加上一个tail(列表)。如下列表的声明等价
```scala
scala> 9::4::2::Nil
res5: List[Int] = List(9, 4, 2)
scala> List(9,4,2)
res6: List[Int] = List(9, 4, 2)
scala> 9::List(4,2)
res7: List[Int] = List(9, 4, 2)
集合操做
(1)向后追加元素(:+),向前追加元素(+:) (只用于向插入顺序
有关的集合中追加)
scala> val v1 = Vector(1,2,3,4) scala> v1 :+ 8 res27: scala.collection.immutable.Vector[Int] = Vector(1, 2, 3, 4, 8) scala> 9 +: v1 res28: scala.collection.immutable.Vector[Int] = Vector(9, 1, 2, 3, 4)
(2)+
操做符,向Set等与插入顺序无关的集合中追加元素
scala scala> val s1 = Set(1,2,3,4) scala> s1 +9 res31: scala.collection.immutable.Set[Int] = Set(1, 9, 2, 3, 4)
(3)++
与--
分别为向集合中追加多个元素
reduceLeft
和reduceRight
(1)coll.reduceLeft(op)表达式,将op函数相继应用到集合中的元素,如图造成一个树形结构
(2)reduceLeft是从集合的左端开始,reduceRight是从集合的右端开始
scala> List(1,7,2,9).reduceLeft(_-_) // 1-7-2-9 scala> List(1,7,2,9).reduceRight(_-_) // 1-(7-(2-9)) res31: Int = -13
foldLeft
和foldRight
(1)折叠方法让调用者能够自定义集合计算的初始元素,进行树型结构的计算
scala scala> List(1,7,2,9).foldRight(5)(_-_) // 1-(7-(2-(9-5))) = -8 scala> List(1,7,2,9).foldLeft(5)(_-_) // 5-1-7-2-9 res33: Int = -14
(2)foldLeft
和foldRight
的简写形式:/:
和:\
scala> (5 /: List(1,2,3))(_-_) // 5-1-2-3 res35: Int = -1(3)利用折叠方法计算词频
scala scala> (Map[Char,Int]() /: "Mississippi"){(m,c) => m + (c -> (m.getOrElse(c,0)+1))} res43: scala.collection.mutable.Map[Char,Int] = Map(M -> 1, s -> 4, p -> 2, i -> 4)
scanLeft
和scanRight
将折叠和映射组合在一块儿,得出每一次计算的中间结果
scala> List(1,2,3,4).scanLeft(0)(_-_) res46: List[Int] = List(0, -1, -3, -6, -10)
定义:
(1)Stream是一个尾部被懒计算的不可变列表,经过操做符#::
能够构造一个流
(2)Strea的尾部懒计算后会缓存起来
scala> val tenMore = numsFrom(10) tenMore: Stream[BigInt] = Stream(10, ?) scala> tenMore.tail.tail res51: scala.collection.immutable.Stream[BigInt] = Stream(12, ?) res52: Stream[BigInt] = Stream(10, 11, 12, ?) // 10,11,12已通过缓存
(3)用take得到多个答案,而后用force强制求值
scala scala> tenMore.take(5).force res54: scala.collection.immutable.Stream[BigInt] = Stream(10, 11, 12, 13, 14)
(4)不要不经take直接调用force,不然会一次计算出stream的全部尾值,知道内存溢出
全部的集合都能经过view方法转换为懒计算的视图,视图不一样于流,连第一个元素也不去计算
scala> (0 to 5) res55: scala.collection.immutable.Range.Inclusive = Range(0, 1, 2, 3, 4, 5) scala> (0 to 5).view // 视图 res56: scala.collection.SeqView[Int,scala.collection.immutable.IndexedSeq[Int]] = SeqView(...) scala> (0 to 5).view.map(_*2) // SeqView(....):全部元素全不计算 res57: scala.collection.SeqView[Int,Seq[_]] = SeqViewM(...) scala> (0 to 5).view.map(_*2).force res58: Seq[Int] = Vector(0, 2, 4, 6, 8, 10)
本章要点:
对于任意一个引用对象v,能够获得一个类型v.type。这个类型有两个可能的值:v(对象自己,但以类型的形式出现) 和 null
对于那种返回this的方法,经过this.type把这些方法串接起来
(1)错误写法
:Document类的setter方法最后返回了this,而该方法的返回值类型若是直接写成Document,虽然能够串联调用setTile和setAuthor,可是一旦出现Document的子类Book,则Book产生的对象调用setatile后返回的类型被写成Document,也就不能串联调用setATitle和setAuthor
// 错误示例 class Document { def setTitle(title: String):Document = { println("set title:" + title) this } def setAuthor(author:String):Documente = { println("set author:" + author) this } } val doc = new Document() doc.setAuthor("lj").setTitle("scala Education") // setAuthor返回的Document类型,能够串联调用setTitle class Book extends Document{ def addChgapter(chapter:String) :Book = { println("add chapter:" + chapter) this } } val book = new Book() book.setTitle("another book").addChgapter("1 chapter") // 编译报错,由于setTitle返回的类型是Document,而Document类没有addChapter方法
(2)正确写法
:为了使继承Document的Book类的对象也能串联调用,能够改造这些setter方法的返回值为this.type,这样,Book类的对象book在调用setTitle方法时,返回的类型就是book.type,而因为book对象有一个addChapter方法,所以能够串接起来
class Document { def setTitle(title: String):this.type = { println("set title:" + title) this } def setAuthor(author:String):this.type = { println("set author:" + author) this } } class Book extends Document{ def addChgapter(chapter:String) :this.type = { println("add chapter:" + chapter) this } } val book = new Book() book.setTitle("another book").addChgapter("1 chapter")
(3)其次,若是想要定义一个接收object实例做为参数的方法,也可使用单例类型。那么为何对于单例对象的方法不直接调用,还要传进一个object对象做为参数在调用呢?由于有人喜欢构造那种调用起来像是一句话的代码
book set Title to "Scala for the impatient"
object Title class Document { private var useNextArgAs:Any = null // 用Title.type声明传入的是Title单例对象, 用this.type声明返回值使得集成类的setter方法也能串联调用 def set(obj:Title.type):this.type = { useNextArgAs = obj this } def to(arg:String) : Unit={ if (this.useNextArgAs==Title) println("set finish") else "" } } object Test extends App{ val doc = new Document() doc set Title to "scala for the impatient" // 构造英文语句 }
scala中,嵌套类属于它包含的外部对象,即每一个实例都有本身的内部类
以下,chatter.member和myFace..member是不一样的类。不能讲任何一个网络(NetWork)的成员(Member)加到另外一个网络中
```scala
class NetWork{
class Member(val name:String) {
val contacts = new ArrayBuffer[Member] // 这个泛型Member,指的是[对象.Member]
}
private val members = new ArrayBuffer[Member]
def join(name:String) :Member= {
val m = new Member(name)
members += m
m
}
}
val chatter = new NetWork
val myFace = new NetWork
val Fred = chatter.join("Fred")
val Barney = myFace.join("Barney")
Fred.contacts += Barney
```
内部类从属于每一个对象这种约束是默认存在的,若是不想要这种约束,应该把Member类挪到NetWork类的外面。更好的选择是在Network的伴生对象中。若是想使用更为松散的定义,能够用类型投影
NetWork#Member,表示任何Network的Member
scala class NetWork{ class Member(val name:String) { val contacts = new ArrayBuffer[NetWork#Member] //val contacts = new ArrayBuffer[Member] // 这个泛型Member,指的是[对象.Member] } ... }
HashMap[String,(Int,Int)]
,可使用type关键字建立一个简单别名,eg:index
。class Book{ import scala.collection.mutable._ type index = mutable.HashMap[String,(Int,Int)] }
结构类型是一组关于抽象方法,字段和类型的规格说明
,这些抽象方法,字段,类型是该规格类型必须具有的。
写法上:用大括号包围这些抽象方法,字段,类型
下例所示,appendlines方法的形参是任何具备append方法的对象和一个string泛型的iterater,appendlines会调用这个对象的append方法
def appendLines(target: {def append(str:String): Any},lines:Iterable[String]): Unit ={ for(l <- lines){ target.append(l);target.append("\n") } }
鸭子类型就像python这种动态类型语言,变量没有类型,当你写下obj.quack()时,运行时回去检查obj指向的对象在那一刻是否具备quack方法。换句话说:你不须要把obj声明为Duck类型,只要它运行时有Duck的方法(走起来,叫起来像鸭子同样)
符合类型的定义形式以下: \(T_1\) with \(T_2\) with \(T_3\) ...,表示要成为该复合类型的实例,必须知足每个类型的要求(好比实现了这几个特质的方法),所以,符合类型也称做交集类型
val images = new ArrayBuffer[java.awt.Shape with java.io.Serializable] val rect = new Rectangle(5,10,20,30) // Rectangle extends Rectangle2D implements Shape,java.io.Serializable images += rect
new ArrayBuffer[java.awt.Shape with java.io.Serializable {def setBounds(x: Int, y: Int, width: Int, height: Int):Unit}]
表示这个ArrayBuffer里的对象既要知足Shape和Serializable接口,还要存在setBounds方法
(1)scala提供了一种让类型的描述趋于数学中置表达式形式的写法:用中置表达式组合多个泛型。eg:用String Map Int
来表示Map[String,Int]
(2)写法:泛型1 类 泛型2
(3)中置表达式也能够用来模式匹配,eg:
case class Person[S,T](val name:S,val age:T) val p : String Person Int= Person("摇摆少年梦",19) p match { case "摇摆少年梦" Person 18=> println("matching is ok") case name Person age=> println("name:"+name+" age="+age) }
(1)scala的存在类型是为了与java的类型统配符兼容
(2)写法:在类型表达式后面跟上forSome{},里面包含了type和val的声明,这些声明是对被forSome修饰的类型作一个限制。
下例中,t1的存在类型和t2的类型通配符是等价的,类型通配符是存在类型的语法糖
type t1 = Array[T] forSome { type T<:JComponent} type t2 = Array[_<:JComponent] // 存在类型容许使用更复杂的类型关系 type t3 = Map[T,U] forSome {type T,type U<:T}
有的嵌套类经过类型投影NetWork#Member
,扩大了泛型范围。但一些方法又想把嵌套类局限于每一个对象的嵌套类,就是用存在类型加以限定
val chatter = new NetWork val myFace = new NetWork val Fred = chatter.join("Fred") // 同一个网络下的成员 val Fred2 = chatter.join("Fred2") // 同一个网络下的成员 val Barney = myFace.join("Barney") // 不一样网络 Fred.contacts += Barney def process[M <: n.Member forSome { val n:NetWork }](m1:M,m2:M) = (m1,m2) process(Fred,Fred2) //process(Barney,Fred2) => 错误:process方法经过forSome里面的val n:NetWork限制了n.Member为对象自身的嵌套类,使得方法接收相同网络的成员,拒毫不同网络的成员
(1)自身类型是对特质自身的一种限制,它指出该特质只能被混入哪一个类中,或智能被混入哪一个类的子类中
(2)形式:this: 类型 =>
trait Logged{ def log(msg:String) } trait LoggerException extends Logged{ this:Exception => // 这个this指代混入特质后的对象 def log(){ log(getMessage()) // getMessage方法来自于this,而this又是Exception的子类 } } object Test extends App{ type f = JFrame with LoggerException // 定义类型时不报错,建立对象时会由于LoggerException的自身类型限制而报错 // val v1 = new f // 报错:f类型是JFrame混入LoggerException,而JFrame不是Exception的子类 }
(1)若是自带有自身类型限制的特质被另外一个特质集成,则子特质必须重复写出自身类型,表示本身和父特质同样也有混入的限制
trait ManagedException extends LoggerException{ this:ArrayIndexOutOfBoundsException => // 这里的类型限制要定义成Exception或其子类 def say(){print("aaa")} }
(1)设想一个应用,他须要日志和验证功能,固然,验证功能也须要用到日志。
(2)设计:所以,能够把日志和验证设为2个特质Logger和Auth,这两个特质分别有本身不一样的实现。而真正的应用类App,只要混入这些不一样的实现组合,就能使得App拥有日志和验证功能。而Auth须要用到Logger,因此在Auth特质中,经过自身类型调用Logger类型的方法
trait Logger{ def log(msg:String) } trait Auth{ this:Logger => // 自身类型调用Logger特质的方法 def login(id:String,passwd:String):Boolean } trait FileLogger extends Logger{ override def log(msg: String): Unit = { println(msg) } } trait MockAuth extends Auth{ this:FileLogger => override def login(id: String, passwd: String): Boolean = { if(id.equals("guest")) { log("guest login fail..") false }else true } } object App extends FileLogger with MockAuth{ // 此处经过依赖注入变换实现 def main(args: Array[String]): Unit = { login("guest","123456") } }
(3)这种方法的怪异之处在于,一个App并不是是验证器和文件日志器的组合。更天然地表述方式是使用成员变量来实现功能组件,而不是把App经过混入特质变成一个巨大的类型。
trait LogComponent{ // 最外层的大组件 trait Logger{ def log(msg:String) } val logger:Logger // 抽象变量 class FileLogger extends Logger{ override def log(msg: String): Unit = { println("write in file: "+msg) } } } trait AuthCompnonent{ // 最外层的大组件 this:LogComponent => // 使用抽象变量logger trait Auth{ def login(id:String,passwd:String):Boolean } val auth:Auth // 抽象变量 class MockAuth extends Auth{ override def login(id: String, passwd: String): Boolean = { if (id.equals("guest")){ logger.log("guest cannot login") // 这个logger变量来自于LoggerComponent,究竟是哪一个实现取决于继承的特质 false }else true } } } object App extends LogComponent with AuthCompnonent{ override val logger = new FileLogger // 成员变量 override val auth = new MockAuth // 成员变量 def main(args: Array[String]): Unit = { auth.login("guest","123456") } }
(1)类或特质中,定义一个在子类中被具体化的抽象类型。eg:以下的Reader特质:
trait Reader{ type Contents def read(fileName:String):Contents } class StringReader extends Reader{ override type Contents = String override def read(fileName: String):Contents = Source.fromFile(fileName,"UTF-8").mkString // mkString method return string, corresponding with TYPE contents } class ImageReader extends Reader{ override type Contents = BufferedImage override def read(fileName: String): Contents = ImageIO.read(new File(fileName)) }
(2)固然,在须要子类给出抽象类型的实现这种方法,还能够经过类型参数实现
trait Reader[C]{ def read(fileName:String):C } class StringReader extends Reader[String]{ override def read(fileName: String) = Source.fromFile(fileName,"UTF-8").mkString } class ImageReader extends Reader[BufferedImage]{ override def read(fileName: String) = ImageIO.read(new File(fileName)) }
(1)若是类型是在建立对象时给出时(不存在继承该类的子类),就应当使用类型参数。好比构建HashMap[String,Int]
(2)若是类型是在子类中给出,则使用抽象类型。好比上面的Reader就是子类中给出。
(3)固然还有一种状况是,在子类中给出类型参数。这种方法没什么很差,可是一旦父类或父特质中有多个类型参数,子类的定义就会变得冗长。eg:Reader[File,BufferedImage],这样会使得伸缩性变差
trait Listener{ type Event <: java.util.EventObject } trait ActionListener extends Listener{ type Event = java.awt.ActiveEvent }
(1)隐士转换:以implicit声明的带有单个参数的函数
(2)这个函数自动将一种类型转换成另外一种类型
case class Fraction(a:Int,b:Int){ def *(second:Fraction) = Fraction(second.a*a,second.b*b) } object Test extends App{ implicit def int2Fraction(n:Int) = Fraction(n,1) val result = 3*Fraction(4,5) // Int没有*(Fraction)的方法,但Fraction有*(Fraction)方法 println(result) }
(1)你多想用new File(README"").read
来读取一个文件,可是jdk的File并未提供这个方法。做为java,你只能向Oracle公司提交申请,可是scala却能经过一年隐士转换来丰富这个api
case class RichFile(filepath:String){ def read() = Source.fromFile(filepath).mkString("") } object Test extends App{ implicit def file2RichFile(from:File) = new RichFile(from.getAbsolutePath) new File("README").read() }
(1)当表达式所得值的类型,和所处位置的期待类型不同时
(2)当访问一个不存在的成员或方法时。eg : File.read()
。也就是说当调用a.fun(b)时,若是存在2个隐士转换使得:convert(a)的结果有fun方法,或者存在方法a.fun(convert(b)),则编译器会使用convert(a)隐士转换。由于编译器会把没有调用成员的对象隐士转换
(1)若是在不进行隐士转换的状况下能够经过编译,则不进行隐士转换
(2)变量只能通过一次隐士转换,而不能进行形如convert1(convert2(a))这样的屡次转换
(3)若是两个隐士转换都知足条件,编译器报错。即:convert1(a)b与convert2(a)b都成立
隐士参数是函数的参数列表中,带有implicit标记的形参。此时在调用该方法时,编译器会查询缺省值
case class Delimiters(left:String,right:String) object Test extends App{ def quote(what:String)(implicit delims:Delimiters) = delims.left + what + delims.right println(quote("impatient scala")(Delimiters("《","》"))) // 显式调用 implicit val quoteDelimiters = Delimiters("'","'") // 隐士调用 println(quote("hello world")) }
(1)隐士转换是一个隐士方法,当方法做为参数传入另外一个方法,就造成了含有隐士参数的高阶方法
def smaller[T](a:T,b:T)(implicit order: T => Ordered[T]) = { if(order(a) < b) a else b } println(smaller("a","b"))
(2)隐士转换做为隐士参数的简化声明
def smaller2[T](a:T,b:T)(implicit order: T=>Ordered[T]) = { if(a<b) a else b // 隐士转换自动执行,所以不用显式调用 } println(smaller2(2,4))
(1)形如T:M
的泛型,表示程序的上下文中,存在一个类型为M[T]
的隐式值。一般用于类的泛型限制
(2)类中的方法使用这个上下文界定的隐式值有两种方法:
(i.) 经过定义implicit隐士形参
(ii) 在函数体中使用Predef
类的implicitly()方法,传入上下文界定类型还原出这个隐式值
// method 1 class Pair[T:Ordering](val first:T,val second:T){ def smaller(implicit ord:Ordering[T])= if (ord.compare(first,second)<0) first else second } // method 2 class Pair1[T:Ordering](val first:T,val second:T){ def smaller = if( implicitly[Ordering[T]].compare(first,second) < 0 ) first else second } object Test1 extends App{ println(new Pair1(24,35).smaller) }
(3)用泛型定义的类,在实例化时,编译器会经过成员变量推断出泛型的类型。
(1)类型证实是形如implicit ev T <:< U
的一个隐士参数,其中<:<
还能够是<=<
,<%<
.
(2)三个符号分别表示:T是不是U的子类型,T是否等于U,T是否能够经过隐士转换为U
(1)<:<
,<=<
,<%<
3个符号并不是是语言特性,而是定义在Predef中的三个类
(2)举例:<:<类的定义
下面的三行代码,解释了scala经过<:<类,实现类型证实的过程。
(i.) 首先:定义了一个带有泛型的抽象类<:<
,这个类继承的(From=>To),实际上就是一个Function1
(带有一个形参的函数)
(ii) 其次:初始化了一个singleton_<:<
对象,这个对象的-From
和+To
泛型都是Any,并且复写了apply方法为传入一个Any类型的参数x,而后把x返回出去
(iii)最后:定义了一个隐士转换$conforms[A]
,它的返回值类型为A <:< A
(<:<[A,A]
的中置写法)。该方法就是将<:<[Any,Any]
强转为<:<[A,A]
(即:Function1[Any,Any]
转换为Function1[A,A]
)。而这个泛型A到底可否让编译器推断出来,就是这个类型证实可否经过的关键
(v.)编译器推断:因为<:<类的泛型一个逆变,一个协变。eg:对于<:<[String,AnyRef]
,编译器就能推断出A是String(String逆变成String,AnyRef协变成String)
@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.") sealed abstract class <:<[-From, +To] extends (From => To) with Serializable private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x } implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
def firstLast[T,IR](it:IR)(implicit env: IR<:<Iterable[T]) = (it.head,it.last) println(firstLast(List(1,2,3)))
(1)@implicitNotFound注解加载类上,该类须要时隐士转换函数From->To
的To类。意义在于告知编译器,再不能构建出这个To类时爆出错误信息
(2)例如:
@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.") sealed abstract class <:<[-From, +To] extends (From => To) with Serializable