justtreee / blog

31 stars 8 forks source link

【Scala学习笔记 · 三】包和引入、继承、文件和正则表达式 #8

Open justtreee opened 6 years ago

justtreee commented 6 years ago

第七章 包和引入

1. 包

下面的代码定义了一个cn.scala.xtwy包 在程序的任何地方都可以通过cn.scala.xtwy.Teacher来使用Teacher这个类

package cn{
  package scala{
    package xtwy{
      class Teacher {
      }
    }
  }
}

2. 包的作用域

包可以和其他作用域嵌套,可以访问上层作用域的名称。

package cn{
  package scala{
    //在包cn.scala下创建了一个Utils单例
    object Utils{
      def toString(x:String){
        println(x)
      }
      //外层包无法直接访问内层包,下面这一行代码编译通不过
      //def getTeacher():Teacher=new Teacher("john")
      //如果一定要使用的话,可以引入包
      import cn.scala.xtwy._
      def getTeacher():Teacher=new Teacher("john")
    }
    //定义了cn.scala.xtwy
    package xtwy{
      class Teacher(var name:String) {
        //演示包的访问规则
        //内层包可以访问外层包中定义的类或对象,无需引入
        def printName()={Utils.toString(name)}
      }

    }
  }
}
object Test{
  //scala允许在任何地方进行包的引入,_的意思是引入该包下的所有类和对象
  import cn.scala._
  import cn.scala.xtwy._
  def main(args: Array[String]): Unit = {
    Utils.toString(new Teacher("john").name)
    new Teacher("john").printName()
  }
}

但在Scala中包名是相对的(Java中是绝对的),也就是说:比如创建com.horstmann.collection包,那么编译器在寻找collection.mutable时就不能找到,因为本意是向使用顶级的scala包的collection包,而不是随便什么存在与可访问作用域中的子包。

而这个解决方法之一就是使用包名,以_root_开始:

val subordinates = new _root_.scala.collection.mutable.ArrayBuffer[Employee]

3. 包对象

package cn{
  package scala{
    object Utils{
      def toString(x:String){
        println(x)
      }
      import cn.scala.xtwy._
      def getTeacher():Teacher=new Teacher("john")
    }
    package xtwy{
      class Teacher(var name:String) {
        def printName()={Utils.toString(name)}
      }
    }
  }
}
//利用package关键字定义单例对象
package object Math {
  val PI=3.141529
  val THETA=2.0
  val SIGMA=1.9
}
class Coputation{
  def computeArea(r:Double)=Math.PI*r*r
}

//PI: Double = 3.141529
//THETA: Double = 2.0
//SIGMA: Double = 1.9

4. 包可见性

package cn{
  package scala{
    object Utils{
      def toString(x:String){
        println(x)
      }
      import cn.scala.xtwy._
      def getTeacher():Teacher=new Teacher("john")
    }
    package xtwy{
      class Teacher(var name:String) {
        def printName()={Utils.toString(name)}
      }
    }
  }
}
class Teacher(var name: String) {
  private def printName(tName:String="") :Unit= { println(tName) }
  //可以访问
  def print(n:String)=this.printName(n)
}

object Teacher{
  //伴生对象可以访问
  def printName=new Teacher("john").printName()
}

object appDemo {
  def main(args: Array[String]): Unit = {
    //不能访问
    //new Teacher("john").printName()
  }
}

5. import高级特性

重命名与隐藏方法

import java.util.{ HashMap => JavaHashMap }
//将java.util.HashMap重命名为JavaHashMap

import java.util.{HashMap=> _,_}
//通过HashMap=> _,这样类便被隐藏起来了

隐式引入 每个Scala程序都隐式的引入了以下代码:

import java.lang._
import scala._
import Predef._

练习

  1. 编写一段让你的Scala朋友们感到困惑的代码,使用一个不在顶部的com包。
    
    package com.horstmann.impatient {
    object Funcy {
    def foo {
      println("top level com");
    }
    }
    }
    package scala.com.horstmann.impatient {
    object Funcy {
    def foo {
      println("not top level com");
    }
    }
    }

import scala._ // 如果去掉用输出 "top level com" , 否则输出 "not top level com" object Test { def main(args : Array[String]) { com.horstmann.impatient.Funcy.foo } }


3. 编写一个包random,加入函数nextInt():Int、nextDouble: Double和setSeed(seed:Int):Unit。生成随机数的算法使用线性同余生成器:
后值=(前值 x * a + b) mod 2^n
其中,a=1664525,b=1013904223,n=32,前值的初始值为seed。

```scala
package random {
  package object random {
    val a : Long = 1664525;
    val b : Long = 1013904223;
    val n : Int = 32;
    var prev : Int = 1;

    def nextInt() : Int = {
      val rand = (prev * a + b) % n
      setSeed(rand.toInt)
      rand.toInt
    }

    def setSeed(seed : Int) : Unit = {
      this.prev = seed
    }

    def nextDouble() : Double = {
      nextInt.toDouble / n
    }
  }
}

object Test {
  def main(args : Array[String]) {
    import random._   //导入包
    random.setSeed(1);
    for (_ <- 1.to(10, 1)) {
      println(random.nextInt);
    }
    for (_ <- 1.to(10, 1)) {
      println(random.nextDouble);
    }
  }
}
justtreee commented 6 years ago

第八章 继承

要点包括:

1. 类的继承

class Person(name: String, age: Int){}
class Student(name: String, age: Int, var studentNo: String) extends Person(name,age){}
object Test {
  def main(args: Array[String]) : Unit = {
    val stu = new Student("XXX",18,"2015")
  }
}

2. 继承的构造函数构造顺序

class Person(name: String, age: Int){
  println("Person")
}
class Student(name: String, age: Int, var studentNo: String) extends Person(name,age){
  println("Student")
}
object Test {
  def main(args: Array[String]) : Unit = {
    val stu = new Student("XXX",18,"2015")
  }
}

//Person
//Student

可见构造Student类之前,首先会调用Person的主构造方法。

3. 方法重写

有时候子类从父类继承过来的方法不能满足需要,就需要方法重写。方法重写是实现多态和动态绑定的关键。利用override关键词实现。

class Person(name: String, age: Int){
  def f() = println("Person")
}
class Student(name: String, age: Int, var studentNo: String) extends Person(name,age){
  override def f() = println("Student")
}
object Test {
  def main(args: Array[String]) : Unit = {
    val stu = new Student("XXX",18,"2015")
    stu.f()
  }
}

//Student

如果父类是抽象类(也就是说方法没有实现),那么子类的方法重写可以不加关键字。

abstract class Person(name: String, age: Int){
  def f(): Unit
}
class Student(name: String, age: Int, var studentNo: String) extends Person(name,age){
  def f() = println("Student")
}
object Test {
  def main(args: Array[String]) : Unit = {
    val stu = new Student("XXX",18,"2015")
    stu.f()
  }
}

//Student

4. 多态与动态绑定

多态”(Polymorphic)也叫“动态绑定”(Dynamic Binding)、“迟绑定”(Late Binding),指“在执行期间(而非编译期间)判断所引用对象的实际类型,根据其实际类型调用其相应的方法。”即指子类的引用可以赋给父类,程序在运行时根据实际类型调用对应的方法 。

abstract class Person(var name: String, var age: Int){
  def f(): Unit
  def f2(p:Person):Unit
}
class Student(name: String, age: Int) extends Person(name,age){
  private var studentNo:Int = 0
  def f() = println("Student f()")
  def f2(p:Person) = {
    println("Student f2()")
    println(this.name+" -> "+p.name)
  }
}
class Teacher(name: String, age: Int) extends Person(name,age){
  private var teacherNo:Int = 0
  def f() = println("Teacher f()")
  def f2(p:Person) = {
    println("Teacher f2()")
    println(this.name+" -> "+p.name)
  }
}
object Test {
  def main(args: Array[String]) : Unit = {
    val p1:Person = new Teacher("Tea", 40)
    val p2:Person = new Student("Stu", 16)
    p1.f2(p2)
    p2.f2(p1)
  }
}

//Teacher f2()
//Tea -> Stu
//Student f2()
//Stu -> Tea

5. 组合和继承

继承可以重用父类的代码,从而简化程序设计,继承是is-a的关系,apple is a kind of fruit(苹果是一种水果)。还有一种代码重用的方式是组合,组合是has-a的关系(one person has a head)。继承在前面已经讲了,这边只给出组合的使用代码:

class Head
class Body
class Hand
//....

//Person类
abstract class Person(var name:String,var age:Int){
  //各类的实例作为该类对象的一部分,通过各类的实例方法实现代码重用
  val head:Head=null  
  val body:Body=null
  val hadn:Hand=nulll
  //....
}

练习

  1. 扩展如下的BankAccount类,新类CheckingAccount 对每次存款和取款都收取1美元的手续费。

    class BankAccount(initialBalance : Double) {
    private var balance = initialBalance
    
    def deposit (amount : Double) = {
    balance += amount
    balance
    }
    
    def withdraw(amount : Double) = {
    balance -= amount
    balance
    }
    }

    答:

    
    class BankAccount(initialBalance : Double) {
    private var balance = initialBalance
    
    def deposit (amount : Double) = {
    balance += amount
    balance
    }
    
    def withdraw(amount : Double) = {
    balance -= amount
    balance
    }
    }

class CheckingAccount(initialBalance: Double) extends BankAccount(initialBalance){ private var balance = initialBalance

override def deposit(amount: Double): Double = { balance = super.deposit(amount)-1 balance }

override def withdraw(amount: Double): Double = { balance = super.withdraw(amount)-1 balance }

}

object Test extends App{ val a = new CheckingAccount(100) println(a.deposit(100)) println(a.withdraw(50)) }

//199.0 //149.0


2. 扩展前一个练习中的BankAccount 类, 新类SavingAccount 每个月都有利息产生 (earnMonthlyInterest方法被调用),并且有每月三次免手续费的存款和取款。在earnMonthlyInterest 方法中重置交易计数
```scala
class BankAccount(initialBalance : Double) {
  private var balance = initialBalance

  def deposit (amount : Double) = {
    balance += amount
    balance
  }

  def withdraw(amount : Double) = {
    balance -= amount
    balance
  }
}
class SavingAccount(initialBalance: Double) extends BankAccount(initialBalance)
{
  private var balance = initialBalance
  private var cnt = 0
  private var interests = balance * 0.018

  override def deposit(amount: Double): Double = {
    balance += amount
    if(cnt > 3){
      balance -= 1
    }
    cnt += 1
    balance
  }

  override def withdraw(amount: Double): Double = {
    balance -= amount
    if(cnt >= 3){
      balance -= 1
    }
    cnt += 1
    balance
  }

  def earnMonthlyInterest():Double = {
    cnt = 0
    balance += interests
    balance
  }
  def show() = {
    println(balance)
  }
}

object Test extends App{
  val a = new SavingAccount(100)
  println(a.deposit(100))
  println(a.withdraw(50))
  a.show()
  println(a.deposit(100))
  a.show()
  println(a.withdraw(50))
  a.show()
  a.earnMonthlyInterest()
  a.show()
  println(a.deposit(100))
  a.show()
}
  1. 定义一个抽象类Item, 加入方法 price 和description. SimpleItem 是一个在构造器中给出价格和描述的物件,利用val可以重写def方法, Bundle是一个可以包含其他物件的物件,其价格是打包中所有物件的价格之和。同时提供一个将物件添加到打包当中的机制,以及一个合适的description 方法。
    
    abstract class Item{
    def price: Double
    def description: String
    }

class SimpleItem(private val iPrice:Double, private val iDescription: String) extends Item{ override def description: String = iDescription

override def price: Double = iPrice }

class Bundle extends Item{ private val items : scala.collection.mutable.ArrayBuffer[SimpleItem] = new scala.collection.mutable.ArrayBuffer[SimpleItem]() def packItems(item: SimpleItem){ items += item } def price() = { var sum:Double = 0 for(i <- items){ sum += i.price } sum }

def description() = { var desc = new scala.collection.mutable.ArrayBuffer[String](); for (i <- items) { desc += i.description }

desc.mkString(",")

} }

object Test extends App{ val b = new Bundle() b.packItems(new SimpleItem(20, "item001")) b.packItems(new SimpleItem(30, "item002")) b.packItems(new SimpleItem(40, "item003"))

println(b.price()) println(b.description()) }

//90.0 //item001,item002,item003


5. 设计一个Point 类, 其 x 和 y 坐标可以通过构造器提供,提供一个子类 LabeledPoint , 其构造器接收一个标签值和 x, y 坐标, 比如: new LabeledPoint(“Black Thursday”, 1929, 230.07)
```scala
abstract class Point(val x:Double, val y:Double){

}
class LabeledPoint(var Label:String, x:Double, y:Double) extends Point(x,y){

}
object Test extends App{
  val p = new LabeledPoint("XXX",1,1)
  println(p.Label+", "+p.x+", "+p.y)
}

//XXX, 1.0, 1.0
justtreee commented 6 years ago

第九章 文件和正则表达式