Scala面向对象编程
类
- 注:class前面没有修饰符,如:public
class Counter{
private var value = 0
def increment(step:Int):Unit = { value += step }
def current():Int = { value }
}
val myCounter = new Counter
myCounter.value = 5
myCounter.increment(3)
myCounter.current // 调用无参数方法时,可以省略方法名后的括号
class Student1 {
private var stuName: String = "Tom"
private var stuAge: Int = 20
def getStuName(): String = stuName
def setStuName(newName: String) = this.stuName = newName
def getStuAge(): Int = stuAge
def setStuAge(newAge: Int) = this.stuAge = newAge
}
// 如果开发main方法进行测试,需要将main方法写到类的伴生对象,即该类的Object对象,相当于定义了static
object Student1 {
// 这里的方法都是静态的
def main(args: Array[String]): Unit = {
val s1 = new Student1
// 第一次输出
println(s1.getStuName())
// 调用set方法
s1.setStuName("Mary")
// 第二次输出
println(s1.getStuName())
// 第三次输出
println(s1.stuName) // 可以直接访问private成员
}
}
内部类
// 学生类,学生有学科的成绩
class Student2 {
// 内部类,记录学生的成绩
// 一个主构造器,且有两个参数:courseName、grade,相当于定义了两个属性
class Course(val courseName: String, val grade: Int){
}
// 学生基本属性
private var stuName: String = "Tom"
private var stuAge: Int = 20
// 定义一个变长数组来保存该学生所有的学科成绩
private var courseList = new ArrayBuffer[Course]()
// 定义一个方法,向学生信息中添加课程
def addNewCourse(cname:String, grade:Int): Unit = {
// 创建新的课程
var c = new Course(cname, grade)
// 将课程加入数组
courseList += c
}
}
// 测试
object Student2{
def main(args: Array[String]): Unit = {
// 创建学生
var s2 = new Student2
// 添加课程
s2.addNewCourse("Chinese", 80)
s2.addNewCounse("English", 85)
}
}
属性的get和set方法
-
当定义属性的时候,如果是private,Scala会自动为private的属性生成对应的get和set方法
- 如:
private var stuName: String = "Tom"
,则 - get方法:
stuName
—> 调用:s1.stuName 注意这里的stuName是方法的名字 - set方法:
stuName_
- 如:
-
如果只希望有get而没有set,则可以把属性定义为常量,如:
private val MONEY: Int = 100
-
如果不希望自动生成get和set方法,即该属性只属于这个对象,可以使用
private[this]
-
成员可见性
/* getter -> value setter -> value_ */ class Counter{ private var privateV = 0 def value = privateV def value_ = (newV:Int){ privateV = newV } def increment(step:Int):Unit = { privateV += step } def current():Int = { privateV } } val myCounter = new Counter myCounter.value_=(3) // 为privateV设置新值 myCounter.value = 3 //当value和value_=成对出现时可以这样使用 println(myCounter.value) // 获取privateV的当前值
-
def 方法名(参数列表):返回结果类型 = {方法体}
-
方法参数前不能加val或var,所有方法参数都是不可变类型
-
方法没有参数则可以省略括号,调用时也不能有括号;若定义时有括号,则调用是可带可不带
-
若方法只有一个参数,可以省略点号(.),用中缀操作符调用方法
myCouner increment 5
-
当方法的返回类型可以从表达式中推断时,可省略结果类型
def current() = value
-
若返回类型为Unit,可以同时省略结果类型和等号
def increment(step:Int){value += step}
构造器
-
主构造器:和类的声名在一起,且只能有一个主构造器;
class Counter(var name:String) {}
,可以使用var
或val
关键字;有关键字则参数自动成为类的字段,可以访问或更改 -
var myC = new Counter("Runner")
-
辅助构造器:可有多个,通过关键字
this
来定义,每个辅构造器的第一个表达式必须调用此前已定义好的一个辅构造器或主构造器class Counter { private var value = 0 private var name = "" private var step = 1 //计算器的默认递进步长 println("the main constructor") def this(name: String){ //第一个辅助构造器 this() //调用主构造器 this.name = name printf("the first auxiliary constructor,name:%s\n",name) } def this (name: String,step: Int){ //第二个辅助构造器 this(name) //调用前一个辅助构造器 this.step = step printf("the second auxiliary constructor,name:%s,step:%d\n",name,step) } def increment(step: Int): Unit = { value += step} def current(): Int = {value} }
Object对象
-
Scala单例对象 <==> Java静态成员
-
Scala中没有静态修饰符,但是object对象中所有的方法都是静态的
-
// 生成id object Person { private[this] var lastId = 0 //一个人的身份编号 def newPersonId() = { lastId +=1 lastId } def main(args: Array[String]): Unit = { Person.newPersonId() // 1 Person.newPersonId() // 2 Person.newPersonId() // 3 } } // 使用应用程序对象,省略main方法 object HelloWorld extends App{ print("Hellow World") }
类的apply方法
-
伴生对象与孤立对象
-
-
apply:将apply方法写在伴生类中,则创建类时可以省略
new
关键字-
class Car(name: String) { def info() { println("Car name is "+ name) } } object Car { def apply(name: String) = new Car(name) //调用伴生类Car的构造方法 } object MyTestApply{ def main (args: Array[String]) { val mycar = Car("BMW") //没有`new`关键字,则自动调用伴生对象中的apply方法 mycar.info() //输出结果为“Car name is BMW” } }
-
-
update
-
val myStrArr = new Array[String](3) //声明一个长度为3的字符串数组,每个数组元素初始化为null myStrArr(0) = "BigData" //实际上,调用了伴生类Array中的update方法,执行myStrArr.update(0,"BigData")
-
-
unapply
-
class Car(val brand:String,val price:Int) { def info() { println("Car brand is "+ brand+" and price is "+price) } } object Car{ def apply(brand:String,price:Int)= { println("Debug:calling apply ... ") new Car(brand,price) } def unapply(c:Car):Option[(String,Int)]={ println("Debug:calling unapply ... ") Some((c.brand,c.price)) } } object TestUnapply{ def main (args: Array[String]) { var Car(carbrand,carprice) = Car("BMW",800000) println("brand: "+carbrand+" and carprice: "+carprice) } }
-
继承 extends
- 基本的继承
class Person(val name:String, val age:Int) {
def sayHello():String = "Hello " + name + " and the age is " + age
}
// 定义子类
// 若果希望子类的参数值覆盖父类的参数,则加`override`关键字
class Employee(override val name:String, val age:Int, val salary:Int) extends Person(name, age){
override def sayHello(): String = "子类的sayHello方法" // 同样需要加`override`
}
object Demo {
def main(args: Array[String]): Unit = {
// Person对象
val p1 = new Person("Tom", 23)
println(p1.sayHello())
// Employee对象
val p2:Person = new Employee("Mike", 25, 1000)
println(p2.sayHello())
// Person的匿名子类
val p3:Person = new Person("jerry", 26){
// 重写sayHello方法
override def sayHello(): String = "匿名子类中的sayHello方法"
}
}
}
- 使用抽象类,关键字
abstract
- 抽象类中包含抽象方法,抽象类只能用来继承
abstract class Vehicle{
// 抽象方法
def checkType():String
}
// 子类中时序父(抽象)类的抽象方法
class Car extends Vehicle{ // 若没有实现,则子类也要定义成抽象类
def checkType():String = {"I am a Car"}
}
class Bike extends Vehicle{
def checkType():String = {"I am a Bike"}
}
object Demo2{
def main(args: Array[String]): Unit = {
val v1:Vehicle = new Car
println(v1.checkType())
val v2:Vehicle = new Bike
println(v2.checkType())
}
}
- 抽象字段:没有初始值的字段
- 子类在继承时,必须提供对应抽象字段的值
abstract class Person{
val id:Int
val name:String
}
// 方法一
class Employee extends Person{
val id:Int = 1
val name:String = "Tom"
}
// 方法二
class Employee(val id:Int) extends Person{
val name:String = "Tom"
}
特质
-
本质就是抽象类,但支持多重继承
-
类似java的接口
-
实现单个继承为
extends
,实现多个用with
trait Flyable { var maxFlyHeight:Int //抽象字段 def fly() //抽象方法 def breathe(){ //具体的方法 println("I can breathe") } } trait HasLegs { val legs:Int //抽象字段 def move(){printf("I can walk with %d legs",legs)} } class Animal(val category:String){ def info(){println("This is a "+category)} } class Bird(flyHeight:Int) extends Animal("Bird") with Flyable with HasLegs{ var maxFlyHeight:Int = flyHeight //重载特质的抽象字段 val legs = 2 //重载特质的抽象字段 def fly(){ printf("I can fly at the height of %d",maxFlyHeight) }//重载特质的抽象方法 }
包对象
-
包的定义
-
可以像java一样使用,如:
package com.my.io class.XXX
-
可以像C#的namespace一样使用package语句,如:
package com.my.io{ class XXX }
-
package也可以是嵌套的,如:
package com.my.io{ class XXX package test{ class T } }
-
-
包的引入:
import
import com.my.io.XXX // 可以不写`XXX`的全路径 import com.my.io._ // 引用`com.my.io`下的所有类 import com.my.io.XXX._ // 引用com.my.io.XXX的所有成员
-
scala中
import
可以写在程序的任意地方def method(fruit:Fruit){ import fruit._ println(name) }
-
-
包对象
- 包可以包含类、对象和特质,但不能包含函数或者变量的定义;包对象正是为了解决这个局限
- Scala中的包对象:常量、变量、方法、类、对象、特质
package class4 // 定义一个包对象 package object MyPackageObject{ val x:Int = 0 var y:String = "Hello World" def sayHelloWorld():String = "Hello World" class MyTestClass{ } object MyTestObject{ } trait MyTestTrait{ } } class Demo{ def method() = { import class4.MyPackageObject._ var a = MyTestClass } }
文件访问
-
读取行
val source = scala.io.Source.fromFile("d:\\a.txt") val lines = source.getLines() for(l <- lines) println(l.toString)
-
读取字符
val source = scala.io.Source.fromFile("d:\\a.txt") for (c <- source) println(c)
-
从URL或其他源读取:注意指定字符集UTF-8
val source = scala.io.Source.fromURL("https://www.baidu.com", "UTF-8") println(source.mkString)
-
读取二进制文件:Scala中不支持直接读取二进制,但可以通过调用Java的InputStream来进行读入
val file = new File("D:\\test.war") val in = new FileInputStream(file) val buffer = new Array[Byte](file.length().toInt) in.read(buffer) in.close()
-
写入文本文件
val out = new PrintWriter("D:\\x.txt") for(i <- 1 to 10) out.println(i) out.close()