Scala 自学笔记2_类
来源:互联网 发布:js开发工具 编辑:程序博客网 时间:2024/05/29 10:46
1、类
class Counter{ private var value = 0 // 必须初始化字段 def increment() { value += 1} //方法默认是公有的 def corrent() = value} //类无需声明为public , 源文件可以包含多各类,所有类都具有公有可见性。val myCounter = new Counter // 或 new Counter()myCounter.increment //或 myCounter.increment() ,对于不会改变对象状态的方法,去掉()是不错的风格 //可以通过以不带()的方式声明来强制这种风格class Counter { ... def current = value // 定义中不带()} //这样 使用者必须用 myCounter.current
getter 和 setter
class Person{ var age = 0 // 公有,Scala 默认生成 get和set, 分别为 age 和 age_= //如果定义为私有,那么 get 和 set 也将变为私有}println(fred.age) // 调用了fred.age()fred.age = 21 //调用了 fred.age_(21) 自定义getter 和 setterclass Person{ private var privateAge = 0 // 设为私有并改名 def age = privateAge def age_=(new Value:Int){ if(new Value > privateAge) privateAge = newValue; //不能变年轻 }}只带getterclass Message{ val timeStamp = new Java.util.Date // 用 val}总结:, 四个选择1、var foo // getter 和 setter2、val foo // getter3、自定义 foo 和 foo_= 方法4、自定义 foo 方法
对象私有字段
class Counter{ private var value = 0 def increment() { value += 1} def isLess(other: Counter) = value < other.valule // 可以访问另一个对象的 私有字段} // 之所以访问other.value是合法的,是因为other也同样是Counter对象更严格的访问限制, 通过private[this] 修饰来实现private [this] var value = 0 //如此一来 Counter类的方法只能访问到当前对象的value字段,而不能访问同样是Counter类型的其他对象的该字段。类私有字段 生成私有getter 和 setter, 但对象私有的字段,不会生成。
Bean属性
虽然Scala会提供getter和setter方法,但这不是java工具所预期的,,如果要更具JavaBeans规范生成getFoo/setFoo方法,参照如下:import scala.reflect.BeanPropertyclass Person{ @BeanProperty var name:String = _}将生成四个方法:1. name:String2. name_=(newValue:String):Unit3. getName() :String4. setName(newValue:String):Unit如果使用主构造器参数的方式定义了某字段,并且需要JavaBean版的getter 和 setter 的方法:class Person(@BeanProperty var name:String)
辅助构造器
class Person{ private var name = "" private var age = 0 def this(name: String){ this() //调用主构造器 this.name = name } def this(name: String, age:Int){ this(name) //调用前一个辅助构造器 this.age = age }}val p1 = new Person //主构造器val p2 = new Person("Fred") // 第一个辅助构造器val p3 = new Person("Fred", 42) // 第二个辅助构造器
主构造器
主构造器的参数直接放置在类名之后import scala.collection.mutable.ArrayBufferclass Networ{ class Member(val name: String){ val contacts = new ArrayBuffer[Member] } private val members = new ArrayBuffer[Member] def join(name:String) = { val m = new Member(name) members += m m }}val chatter = new Networkval myFace = new Networkchatter.Member 和 myFace.Member 是不同的两个类val fred = chatter.join("Fred")val wilm = chatter.join("Wilma")fred.contacts += wilma // okval barney = myFace.join("Barney") // 类型为myFace.Memberfred.contacts += barney //错误,不能将一个myFace.Member 添加到chatter.Member元素缓冲中如果希望突破这个限制,可以将嵌套类移到其他地方,一个不错的位置是 伴生对象object Network{ class Member(val name:String){} //类似java的静态对象,挂在类上,而不是 对象上。}class Network{ private val members = new ArrayBuffer[Network.Member]}或者是投影class Network { class Member(val name: String) { val contacts = new ArrayBuffer[Network#Member] }}class Person(val name:String , val age:Int){}主构造器会执行类定义中的所有语句class Person(val name:String, val age: Int){ println("Just constructed another person") def description = name + " is " + age + " years old"}class MyProg{ private val props = new Properties props.load(new FileRecoder("myprog.properties"))}class Person(val name:Stirng , private var age:Int)class Person(name:String, age:Int) { // 这里没有给val 或 var修饰,但是在它们至少被一个方法使用,所以升格为字段,并对象私有,同等于private[this] val字段 def description = name + " is " + age + " years old" }//如果它们没被使用,将不保存为字段,仅仅是主构造器中的代码访问的普通参数。主构造器参数生成的字段和方法name: String对象私有字段。如果没有方法使用name,则没有该字段private val/var name: Sting私有字段,私有的getter/setter方法val/var name:String私有字段,公有的getter/setter方法@BeanProperty val/var name:String私有字段,公有的Scala版和JavaBeans版的getter/setter方法私有的主构造器class Person private(val id:Int) {} //这样 用户必须通过辅助构造器了
嵌套类
import scala.collection.mutable.ArrayBufferclass Networ{ class Member(val name: String){ val contacts = new ArrayBuffer[Member] } private val members = new ArrayBuffer[Member] def join(name:String) = { val m = new Member(name) members += m m }}val chatter = new Networkval myFace = new Networkchatter.Member 和 myFace.Member 是不同的两个类val fred = chatter.join("Fred")val wilm = chatter.join("Wilma")fred.contacts += wilma // okval barney = myFace.join("Barney") // 类型为myFace.Memberfred.contacts += barney //错误,不能将一个myFace.Member 添加到chatter.Member元素缓冲中如果要突破这个先知,可以将嵌套类移到别处,一个不错的选择是 伴生对象object Network{ class Member(val name:String){}}class Network{ private val members = new ArrayBuffer[Network.Member]}或者是使用 类型投影Network#Member,其含义是“任何Network的Member”class Network{ class Member(val name:String){ val contacts = new ArrayBuffer[Network#Member] }}<pre name="code" class="java">在嵌套类中,可以通过外部类.this 的方式来访问外部类的this引用.class Network(val name:String) { outer -> // outer 变量指向 Network.thisclass Member(val name:String){ ... def description = name + "inside " + outer.name }}
2、对象
单例对象
Scala没有静态方法或静态字段,可以用objectg 这个语法来达到相同的目的object Accounts { privagte var lastNumber = 0 def newUniqueNumber() = { lastNumber += 1; lastNumber }<pre name="code" class="java">}Account.newUniqueNumber()对象的构造器在该对象第一次被使用时调用,这里即首次调用newUniqueNumber时执行,如果对象从未被使用,那么其构造器不会被执行对象可以拥有类的所有特性, 可以扩展其他类或特质,唯一的例外就是:不能提供构造器参数常用的方式与 Java中使用单例的地方相似:1、存放工具函数或常量的地方2、高效地共享单个不可变实例3、需要用单个实例来协调某个服务时(单例模式)
在java中,可以既有实例方法又有静态方法,在Scala,就是伴生对象来完成:
class Accout{ val id = Account.newUniqueNumber() private var balance = 0.0 def deposit(amount: Double) { balance += amount} ...}object Account { //伴生对象 private var lastNumber = 0 private def newUniqueNumber() = { lastNumber +=1; lastNumber}}类和它的伴生对象可以互相访问私有特性,必须存在于同一个源文件中
扩展类和特质的对象
abstract class UndoableAction(val description: String){ def undo(): Unit def redo(): Unit}object DoNothingAction extends UndoableAction("Do nothing"){ override def undo(){} override def redo(){}}DoNothingAction 对象可以被所有需要这个缺省行为的地方共有。val actions = Map("open" -> DoNothingAction, "save" -> DoNothingAction, ...)//打开和保存功能尚未实现
apply方法
通常使用类的伴生对形象的apply方法来初始化类,省去new 关键字object(参数1, ..., 参数n)Array("Mary","had","a","little","lamb")Array(Array(1,7), Array(2,9))class Account private (val id:Int, initialBalance: Double){ private var balance =initialBalance} object Account{ def apply(initialBalance : Double) = new Account(newUniqueNumber(), initialBalance) ... }val acct = Accounnt(1000.0)应用启动对象每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String] => Unit:object Hello{ def main(args:Array[String]){ println("Hello, World!") }}<pre name="code" class="java">也可以扩展App特质object Hello extends App{ prinltn("Hello, World!")}如果需要命令行参数,可以通过args属性得到object Hello extends App{ if(args.length > 0) println("Hello, " + argss(0)) else println("Hello, World!")}scalac Hello.scalascala -Dscala.time Hello Fred //scala.time 会显示运行时间Hello, Fred[total 4ms]
枚举
定义一个扩展Enumeration类的对象,并以Value方法调用初始化枚举中的所有可选值object TrafficLightColor extends Enumeration{ val Red, Yellow, Green = value}val Red = Valueval Yellow = Valueval Green = Value每次调用Value方法都返回内部类的新实例,该内部类也叫做Value.或者 也可以向Value方法传入ID 名称val Red = Value(0, "Stop") val Yellow = Value(10) // 名称为Yellowval Green = Value("Go")// id 为11如果不指定ID,则ID在前一个枚举值基础上加一,从零开始,缺省名称 为字段名。可以用TrafficLightColor.Red, 等来引用,如果觉得太冗长,可以直接引入枚举值import TrafficLightColor._
也可以用一种类型别名:object TrafficLightColor extends Enumeration{ type TrafficLightColor = Value val Red, Yellow, Green = Value}现在枚举的类型变成了TrafficLightColor.TrafficLightColor,不过仅当使用import 语句这样做才有意义。import TrafficLightColor._def doWhat(color: TrafficLightColor) ={ if( color == Red) "stop" else if (color == Yellow) "hurry up" else "go"}
枚举值的ID 可以通过id 方法返回,名称通过toString方法返回for(c <- TrafficLightColor.values) println( c.id = ": "+ c)可以通过枚举的ID或名称来进行查找定位,以下两段代码都输出TrafficLightColor.Red对象TrafficLightColor(0) // 将调用Enumeration.applyTrafficLightColor.withName("Red")
3、包和引入
package com{ package horstmann{ package impatient{ class Employee ... } }}packkage com.horstmann.impatient{ //串联式,com和com.horstmann的成员在这里不可见 package people{ classPerson }}源文件的目录和包之间并没有强制的关联关系,不需要将Employee.scala 放在 com/horstmann/impatient目录当中。同时也可以在一个文件当中定义多个包。
作用域规则
package com{ package horstmann{ object Utils{ def percentOf() = ... } package impatient{ class Employee{ ... def giveRaise(rate: scala.Double){ salary += Utils.percentOf(salary, rate) } } } }}这里Utils.percentOf使用的是当前包下的内容,是相对路径,而Java一直是 绝对路径注意:如果有个包定义为collection , 而在使用scala的collection时,使用相对路径,可能报错,这个时候就要使用绝对路径,以_root_开始,如val subordinates = new _root_.scala.collection.mutable.ArrayBuffer[Employee]
文件顶部标记法
package com.horstmann.impatientpackage peopleclassPerson等同于package com.horstmann.impatient{ package people{ class Person }}文件内的所有内容都属于 com.horstmann.impatient.people,但com.horstmann.impatient的包的内容是可见的,可以被直接引用
包对象
包可以包含类、对象和特质,但不能包含函数或变量,包对象就是为了解决这个局限。package com.hostmann.impatientpackage object people{ val defaultName = "John"}package people{ class Person{ var name = defaultName // 从包对象拿到常量 }}
package com.horstmann.impatient.peopleclass Person{ private [people] def description = "A person with name" + name ...}// description在people 包内可见可以将可见度 延展到上层包private [impatient] def description = ...
引入
import java.awt.Colorimport java.awt._ //等同于java的通配符*import java.awt.{Color, Font} //选择性引入import java.util.{HashMap => JavaHashMap} //重命名import java.util.{HashMap=>_,_} //隐藏HashMap,引入其他成员任何地方都可以声明引入class Manager{ import scala.collection.mutable._}
隐式引入每个Scala程序都隐式地如下代码import java.lang._import scala._import Predef._后面引入会覆盖前面重名的,例如scala.StringBuilder 会覆盖 java.lang.StringBuilder 而不是与之冲突。当 我们 使用 collection.mutable.Hashmap其实是在使用scala.collection.mutable.Hashmap
4、继承
使用extends关键字class Employee extends Person{ var salary = 0.0}final 类无法被继承,final方法无法被重写,final字段无法被覆盖。注: java的final字段是不可变,scala 即为 val.
重写一个非抽象方法必须使用 overridepublic class Persion extends xxx{ override def toString = getClass.getName + "[name=" + name + "]"}调用超类方法和Java一样,使用superpublic class Employee extends Person{ override def toString = super.toString + "[salary=" + salary + ";"}
类型检查和转换
isInstanceOf, asInstanceOfif(p.isInstanceOf[Employee]){ val s = p.asInstanceOf[Employee] // s的类型为Employee}如果p是 Employee类或其自雷的对象,isInstanceOf 将会成功如果p是null,则p.isInstanceOf[Employee]返回false, p.asInstanceOf[Employee]返回null如果p不是一个Employee , 则 p.asInstanceOf[Employee] 将跑出异常如果想要测试p指向的 是一个Employee对象,但又不是自雷的话,用p.getClass == classOf[Employee] // classOf定义在 scala.Prede对象中, 所以会被自动引入ScalaJavaobj.isInstanceOf[C1]obj instanceof C1obj.asInstanceOf[C1](C1) objclassOf[C1]C1.class
不过,与类型检查和转换相比, 模式匹配通常是更好的选择:p match{ case s: Employee => ... // 将s 作为Employee 处理 case _ => // p 不是Employee}
受保护字段和方法
和Java一样, protected, 可以被任何子类访问,但不能从其他位置看到。与Java不同,protected的成员对于类所属的包而言,是不看见的, 如果需要这种可见性,可以使用 包修饰:protected [包名] def xxx = xxx;
超类的构造
class Employee(name: String, age: Int, val salary: Double) extends Person(name, age)这里定义了子类 和 调用超类的主构造器。在Scala的构造器中,不能像Java一样 ,调用super(params)。Scala类可以扩展Java类class Square(x:Int, y:Int, width:Int) extends java.awt.Rectangle(x, y, width, width)
def 只能重写另一个defval 只能重写另一个val 或 不带参数的defvar 只能重写另一个抽象的var //换句话说,如果你用了var, 所有的子类只能被动接受class Person(val name: String){ override def toString = getClass.getName + "[name=" + name + "]" }class SecretAgent(codename :String ) extendsd Person(codename){ override val name = "secret" // 覆盖了父类的name override val toString = "secret"}abstract class Person{ def id:Int}class Student ( override val id : Int) extends Person // 覆盖了父类的 id
匿名子类
val alien = new Person("Fred") { def greeting = "Greetings, Earthling! My name is Fred."}从技术上讲,这将会创建一个结构类型的对象, 该对象标记为 Person{def greeting : String}可以用这个类型 作为 参数类型的 定义def meet(p : Person ( def greeting: String)){ println(p.name + "says: "+ p.greeting)}
抽象类
abstract class Person (val name : String) { def id :Int // 没有方法体, 抽象方法,无需像java一样,加上abstract}类中如果有一个方法是 抽象的,那么该类必须声明为abstract子类重写超类的抽象方法,无需overrideclass Employee(name:String) extends Person(name){ def id = name.hashCode // 不需要override}
抽象字段
没有初始值的字段,即为抽象字段abstract class Person{ val id : Int //带有抽象的getter方法 var name: String //带有抽象的 g/s}class Employee (val id: Int) extends Person { // 具体的 id var name = "" //具体的 name , 同样无需 override}用 匿名类型 来 定制 抽象字段:val fred = new Person{ val id = 1729 var name = "Fred"}
构造顺序和提前定义
class Creature { val range: Int = 10 val env:Array[Int] = new Array[Int](range)}class Ant extends Creature{ override val range = 2}执行流程:range 设置为10初始化env数组,调用range()取值器该方法被重写以输出(还未初始化的)Ant类的range字段值range()方法返回0env设定长度为0Ant构造器继续执行,range字段为2这里的问题是在构造器内不应该依赖val的值。这里和Java一样,当在超类的构造方法中调用方法时,会遇到相似的问题,被调用的方法可能被子类重写,因此它可能并不会按照你的语气行事。解决方法:1、val 声明为final , 安全不灵活2、超类中val声明为lazy, 安全不高效3、子类提前定义语法class Ant extends { override val range = 2 // 在超类构造器执行之前初始化子类的val字段}with Creature
与Java中基本类型相对应的类,以及Unit类型,都扩展自AnyVal所有其他类都是AnyRef的子类,AnyVal和AnyRef都扩展自Any类Any类定义了 isInstanceOf、asInstanceOf方法,以及用于相等性判断和哈希码的方法。AnyRef类追加了来自Object类的wart和notify/notifyAll,同时提供了一个带函数参数的方法synchronized,等同于Java中的synchronized块,如account.synchronized{account.balanced += amount}
对象相等性
AnyRef的 eq 方法检查两个引用是否指向同一个对象,AnyRef的equals方法调用eq。在应用程序中,不直接调用eq或equals,只要用==操作符就好override def equals(other : Any) ={ val that = other.asInstanceOf[Item] if(that == null) false else description == that.description && price == that.price}override def hashCode = 13 * description.hashCode + 17 * price.hashCode //定义equals时, 记得同时也定义hashCode
0 0
- Scala 自学笔记2_类
- Scala 自学笔记3_特质
- scala自学笔记(2)
- Scala 自学笔记
- Scala 自学笔记 集合
- Scala 自学笔记 注解
- 三天怼完Scala---Scala自学笔记
- Scala 自学笔记 操作符
- scala自学笔记(1)
- scala 自学笔记 高阶函数
- Scala 自学笔记 模式匹配和样例类
- Scala学习笔记2--类
- ARM9自学笔记_汇编指令1
- linux自学笔记:00_开山篇
- scala自学日记(2)-使用Scala解释器(interpreter)
- Scala的学习笔记_第一天
- Scala的学习笔记_第二天
- 自学javascript笔记_自用_解析W3school的代码
- 用GDB调试程序(六)
- JAVA反射机制
- tornado教程资源
- android读取keystore证书文件
- Nginx透传获取客户端IP地址
- Scala 自学笔记2_类
- Mongo-Hadoop
- C++拾遗--多线程:C语言多线程的引入
- Myql 的严格模式与松散模式设置
- javascript操作字符串函数
- Python单元测试——深入理解unittest
- jQuery UI vs EasyUI
- C++拾遗--引用(左值引用、右值引用)
- C/C++ windows 获取CPU核数