经典的 WordCount 的讲解
示例代码如下:

package com.atguigu.chapter14.homework.wordcount/*val lines = List("atguigu han hello ", "atguigu han aaa aaa aaa ccc ddd uuu")使用映射集合,list中,各个单词出现的次数,并按出现次数排序。*/
object WordCount {def main(args: Array[String]): Unit = {val lines = List("atguigu han hello ", "atguigu han aaa aaa aaa ccc ddd uuu")// 先分步完成,再组合// 1、扁平:将集合中的每个元素的子元素映射到某个函数并返回新的集合。// val res1: List[String] = lines.flatMap((s: String) => s.split(" ")) // 简写形式如下// val res1: List[String] = lines.flatMap(s => s.split(" "))val res1: List[String] = lines.flatMap(_.split(" "))println("res1=" + res1) // res1=List(atguigu, han, hello, atguigu, han, aaa, aaa, aaa, ccc, ddd, uuu)// 2、做成对偶 List,才能分组并统计// val res2 = res1.map((s:String) => (s, 1)) // 简写形式如下// val res2 = res1.map(s => (s, 1))val res2 = res1.map((_, 1))println("res2=" + res2) // res2=List((atguigu,1), (han,1), (hello,1), (atguigu,1), (han,1), (aaa,1), (aaa,1), (aaa,1), (ccc,1), (ddd,1), (uuu,1))// 3、分组。把不同的单词归属到不同的组// val res3 = res2.groupBy((x:(String, Int)) => x._1)  // 简写形式如下// val res3 = res2.groupBy(x => x._1)val res3 = res2.groupBy((_._1))println("res3=" + res3) // res3=Map(han -> List((han,1), (han,1)), ddd -> List((ddd,1)), ..., aaa -> List((aaa,1), (aaa,1), (aaa,1)))// 4、对上面的各个元祖进行统计// val res4 = res3.map((x:(String, List[(String, Int)])) => (x._1, x._2.size)) // 简写形式如下val res4 = res3.map(x => (x._1, x._2.size))println("res4=" + res4) // res4=Map(han -> 2, ddd -> 1, ccc -> 1, uuu -> 1, atguigu -> 2, hello -> 1, aaa -> 3)// 5、排序,使用方法// val res5 = res4.toList.sortBy((x:(String,Int)) => x._2) // 简写形式如下// val res5 = res4.toList.sortBy(x => x._2)val res5 = res4.toList.sortBy(_._2) // 从小到大// val res5 = res4.toList.sortBy(_._2).reverseprintln("rest5=" + res5) // rest5=List((ddd,1), (ccc,1), (uuu,1), (hello,1), (han,2), (atguigu,2), (aaa,3))// 合并println("rest0=" + lines.flatMap(_.split(" ")).map((_, 1)).groupBy((_._1)).map(x => (x._1, x._2.size)).toList.sortBy(_._2))}
}

输出结果如下:

res1=List(atguigu, han, hello, atguigu, han, aaa, aaa, aaa, ccc, ddd, uuu)
res2=List((atguigu,1), (han,1), (hello,1), (atguigu,1), (han,1), (aaa,1), (aaa,1), (aaa,1), (ccc,1), (ddd,1), (uuu,1))
res3=Map(han -> List((han,1), (han,1)), ddd -> List((ddd,1)), ccc -> List((ccc,1)), uuu -> List((uuu,1)), atguigu -> List((atguigu,1), (atguigu,1)), hello -> List((hello,1)), aaa -> List((aaa,1), (aaa,1), (aaa,1)))
res4=Map(han -> 2, ddd -> 1, ccc -> 1, uuu -> 1, atguigu -> 2, hello -> 1, aaa -> 3)
rest5=List((ddd,1), (ccc,1), (uuu,1), (hello,1), (han,2), (atguigu,2), (aaa,3))
rest0=List((ddd,1), (ccc,1), (uuu,1), (hello,1), (han,2), (atguigu,2), (aaa,3))

第十七章 设计模式

17.1 学习设计模式的必要性

17.2 掌握设计模式的层次

       独孤求败五个境界对应人生五个时期,看看你在什么境界?
  第一境界:利剑,“凌厉刚猛,无坚不摧,弱冠前以之与河朔群雄争锋。”
  第二境界:软剑,“紫薇软剑,三十岁前所用,误伤义士不祥,乃弃之深谷。”
  第三境界:重剑,“重剑无锋,大巧不工。四十岁前恃之横行天下。”
  第四境界:木剑,“四十岁后,不滞于物,草木竹石均可为剑。”
  第五境界:无剑,“自此精修,渐进于无剑胜有剑之境。”

17.3 设计模式的介绍

17.4 设计模式的类型

17.5 简单工厂模式(生产类)

17.5.1 基本介绍

17.5.2 看一个具体的需求

17.5.3 使用传统的方式来完成

代码结构:

示例代码:

传统的方式的优缺点:

17.5.4 使用简单工厂模式来完成

简单工厂模式的设计方案: 定义一个实例化 Pizaa 对象的类,封装创建对象的代码。
示例代码如下:
SimplePizzaFactory.scala

package com.atguigu.chapter17.designpattern.simplefactory.pizzastore.userimport com.atguigu.chapter17.designpattern.simplefactory.pizzastore.pizza.{CheesePizza, GreekPizza, PepperPizza, Pizza}object SimplePizzaFactory {// 提供一个创建 Pizza 的方法def createPizza(t: String): Pizza = {var pizza: Pizza = nullif (t.equals("greek")) {pizza = new GreekPizza} else if (t.equals("pepper")) {pizza = new PepperPizza} else if (t.equals("cheese")) {pizza = new CheesePizza}return pizza}
}

OrderPizza.scala 代码修改如下:

package com.atguigu.chapter17.designpattern.simplefactory.pizzastore.userimport com.atguigu.chapter17.designpattern.simplefactory.pizzastore.pizza.{Pizza}import scala.util.control.Breaks._
import scala.io.StdInclass OrderPizza {var orderType: String = _var pizza: Pizza = _breakable {do {println("请输入pizza的类型(使用简单工厂模式):")orderType = StdIn.readLine()pizza = SimplePizzaFactory.createPizza(orderType)if (pizza == null) {break()}this.pizza.prepare()this.pizza.bake()this.pizza.cut()this.pizza.box()}while (true)}
}

17.6 工厂方法模式(生产方法)

17.6.1 看一个新的需求

17.6.2 工厂方法模式介绍

17.6.3 工厂方法模式应用案例

图解1

17.7 抽象工厂模式(生产类)

17.7.1 基本介绍

17.7.3 抽象工厂模式应用案例

17.8 工厂模式小结

将工厂抽象成两层:AbsFactory(抽象工厂) 和 具体实现的工厂子类。

17.9 单例模式

17.9.1 什么是单例模式+单例模式的应用场景

17.9.2 单例模式的应用案例-懒汉式

Scala 中没有静态的概念,所以为了实现 Java 中单例模式的功能,可以直接采用类对象(即伴生对象)方式构建单例对象。
示例代码如下:

package com.atguigu.chapter17.designpattern.singletonobject TestSingleTon1 extends App {val singleTon1 = SingleTon1.getInstanceval singleTon2 = SingleTon1.getInstanceprintln(singleTon1.hashCode() + " " + singleTon2.hashCode())
}// 将 SingleTon1 的构造方法私有化
class SingleTon1 private() {println("~")
}object SingleTon1 {private var s: SingleTon1 = nulldef getInstance = {if (s == null) {s = new SingleTon1}s}
}
/* 底层代码public SingleTon1 getInstance() {if (s() == null) {s_$eq(new SingleTon1());}return s();}*/

输出结果如下:

~
1626877848 1626877848

17.9.2 单例模式的应用案例-饿汉式

示例代码如下:

package com.atguigu.chapter17.designpattern.singletonobject TestSingleTon2 extends App {val singleTon1 = SingleTon2.getInstanceval singleTon2 = SingleTon2.getInstanceprintln(singleTon1.hashCode() + " " + singleTon2.hashCode())
}// 将 SingleTon2 的构造方法私有化
class SingleTon2 private() {println("~~")
}object SingleTon2 {private val s: SingleTon2 = new SingleTon2def getInstance = {s}
}
/* 底层代码public SingleTon2 getInstance() {return s();}*/

输出结果如下:

~~
1626877848 1626877848

17.10 装饰者模式(Decorator)

17.10.1 看一个具体的需求

17.10.2 方案1-较差的方案+小结和分析

方案1-较差的方案

方案1-小结和分析

17.10.3 方案2-好点的方案+小结和分析

方案2-好点的方案

方案2-小结和分析

17.10.4 装饰者模式原理

17.10.5 装饰者模式定义

17.10.6 用装饰者模式设计重新设计的方案

装饰者模式下的订单:2 份巧克力 + 1份牛奶的 LongBlack

17.10.7 装饰者模式咖啡订单项目应用实例

咖啡订单项目包结构

17.10.8 Java 中装饰者模式的经典使用

17.11 观察者模式(Observer)

17.11.1 看一个具体的需求

WeatherData 类

17.11.2 气象站设计方案1-普通方案

代码结构

代码实现
WeatherData.scala

package com.atguigu.chapter17.designpattern.observer.localinternetobserver// 这个类是气象局维护的一个提供天气情况数据的核心类
class WeatherData {private var mTemperatrue: Float = _ // 温度private var mPressure: Float = _ // 气压private var mHumidity: Float = _ // 湿度private var mCurrentConditions: CurrentConditions = _ // 气象局的天气公告板(相当于显示界面,我们使用一个类进行模拟)// 构造 WeatherData 对象时,将 CurrentConditions 传入进来def this(mCurrentConditions: CurrentConditions) {thisthis.mCurrentConditions = mCurrentConditions}def getTemperature() = {mTemperatrue}def getPressure() = {mPressure}def getHumidity() = {mHumidity}def dataChange() = {// 更新天气公告板最新数据mCurrentConditions.update(getTemperature(), getPressure(), getHumidity())}// 气象局更新天气情况的方法(先更新自己的核心类)def setData(mTemperature: Float, mPressure: Float, mHumidity: Float) = {this.mTemperatrue = mTemperaturethis.mPressure = mPressurethis.mHumidity = mHumiditydataChange()  // 再去修改天气公告板最新数据}
}

CurrentConditions.scala

package com.atguigu.chapter17.designpattern.observer.localinternetobserver// 气象局的天气公告板
class CurrentConditions {private var mTemperature: Float = _private var mPressure: Float = _private var mHumidity: Float = _// 更新气象局的天气公告板最新数据,然后进行显示def update(mTemperature: Float, mPressure: Float, mHumidity: Float) = {this.mTemperature = mTemperaturethis.mPressure = mPressurethis.mHumidity = mHumiditydisplay() // 显示操作}// 显示方法def display() = {println("***气象局的天气公告板显示 Today mTemperature: " + mTemperature + "***")println("***气象局的天气公告板显示 Today mPressure: " + mPressure + "***")println("***气象局的天气公告板显示 Today mHumidity: " + mHumidity + "***")}
}

InternetWeather.scala

package com.atguigu.chapter17.designpattern.observer.localinternetobserverobject InternetWeather {def main(args: Array[String]): Unit = {// 创建气象局的天气公告板val mCurrentConditions = new CurrentConditions()// 构造 WeatherData 对象时,将 CurrentConditions 传入进来val mWeatherData = new WeatherData(mCurrentConditions)// 模拟设置最新数据mWeatherData.setData(30, 150, 40)}
}

输出结果如下:

***气象局的天气公告板显示 Today mTemperature: 30.0***
***气象局的天气公告板显示 Today mPressure: 150.0***
***气象局的天气公告板显示 Today mHumidity: 40.0***

气象站设计方案1-普通方案小结与分析

17.11.3 观察者模式原理

17.11.4 气象站设计方案2-观察者模式

设计类图

观察者模式的好处


代码结构

代码实现
Subject.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.myobserver// 气象站-Subject 是一个 trait(接口)
trait Subject {// 注册def registerObserver(o: ObServer)// 移除def removeObserver(o: ObServer)// 通知def notifyObservers()
}

ObServer.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.myobserver// ObServer 是一个接口
trait ObServer {// 是抽象方法,等待其它的观察者去具体实现def update(mTemperatrue: Float, mPressure: Float, mHumidity: Float)
}

WeatherDataSt.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.modeimport com.atguigu.chapter17.designpattern.observer.internetobserver.myobserver.{ObServer, Subject}import scala.collection.mutable.ListBuffer// 气象站 Subject 的实现类-气象站天气数据的类
class WeatherDataSt extends Subject {private var mTemperature: Float = _private var mPressure: Float = _private var mHumidity: Float = _// 用于管理的所有观察者private val mObservers: ListBuffer[ObServer] = ListBuffer()def getTemperature() = {mTemperature}def getPressure() = {mPressure}def getHumidity() = {mHumidity}def dataChange() = {notifyObservers();}def setData(mTemperature: Float, mPressure: Float, mHumidity: Float) = {this.mTemperature = mTemperaturethis.mPressure = mPressurethis.mHumidity = mHumiditydataChange()}override def registerObserver(o: ObServer): Unit = {mObservers.append(o)}override def removeObserver(o: ObServer): Unit = {if (mObservers.contains(o)) {mObservers -= o // 移除}}override def notifyObservers(): Unit = {for (item <- mObservers) {item.update(getTemperature(), getPressure(), getHumidity())}}
}

CurrentConditions.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.modeimport com.atguigu.chapter17.designpattern.observer.internetobserver.myobserver.ObServer// 具体的观察者-气象局自己的公告板
class CurrentConditions extends ObServer {private var mTemperature: Float = _private var mPressure: Float = _private var mHumidity: Float = _override def update(mTemperature: Float, mPressure: Float, mHumidity: Float): Unit = {this.mTemperature = mTemperaturethis.mPressure = mPressurethis.mHumidity = mHumiditydisplay() // 显示操作}// 显示方法def display() = {println("***Today mTemperature: " + mTemperature + "***")println("***Today mPressure: " + mPressure + "***")println("***Today mHumidity: " + mHumidity + "***")}
}

ForcastConditions.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.modeimport com.atguigu.chapter17.designpattern.observer.internetobserver.myobserver.ObServer// 具体的观察者-广播站的公告板
class ForcastConditions extends ObServer {private var mTemperature: Float = _private var mPressure: Float = _private var mHumidity: Float = _override def update(mTemperatrue: Float, mPressure: Float, mHumidity: Float): Unit = {this.mTemperature = mTemperatruethis.mPressure = mPressurethis.mHumidity = mHumiditydisplay()}def display() = {println("***广播播报明天温度:" + mTemperature  + "***")println("***广播播报明天气压:" + mPressure + "***")println("***广播播报明天湿度:" + mHumidity + "***")}
}

InternetWeather.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.modeobject InternetWeather {// 测试def main(args: Array[String]): Unit = {// 先创建气象站 Subject 的实现类对象val mWeatherDataSt = new WeatherDataSt()// 再创建具体的观察者对象val mCurrentConditions = new CurrentConditions()val mForcastConditions = new ForcastConditions()// 通过气象站的实现类对象进行注册,并传入对应的具体观察者对象mWeatherDataSt.registerObserver(mCurrentConditions)mWeatherDataSt.registerObserver(mForcastConditions)mWeatherDataSt.setData(30, 150, 40)// mWeatherDataSt.removeObserver(mCurrentConditions)// mWeatherDataSt.setData(30, 250, 50)// java.util.Observable Java内置观察者模式}
}

输出结果如下:

***Today mTemperature: 30.0***
***Today mPressure: 150.0***
***Today mHumidity: 40.0***
***广播播报明天温度:30.0***
***广播播报明天气压:150.0***
***广播播报明天湿度:40.0***

17.11.5 Java 内置观察者模式

17.12 代理模式(Proxy)

17.12.1 代码模式的基本介绍

17.12.2 看一个具体的需求

17.12.3 完成监控本地糖果机

对本地糖果机的状态和销售情况进行监控,相对比较简单,完成该功能。
代码结构

代码实现
State.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.servertrait State extends Serializable {def insertCoin() // 插入硬币def returnCoin() // 返回硬币def turnCrank() // 转动曲柄def printstate() // 输出状态def getstatename(): String // 返回状态名字def dispense() // 分配状态的,比如:卖出一块糖后,就看看当前糖果机应该进入哪个状态
}

CandyMachine.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.server// 糖果机
class CandyMachine {var mSoldOutState: State = _var mOnReadyState: State = _var mHasCoinState: State = _var mSoldState: State = _var mWinnerState: State = _private var location = ""private var state: State = _private var count = 0def this(location: String, count: Int) {thisthis.location = locationthis.count = countmSoldOutState = new SoldOutState(this);mOnReadyState = new OnReadyState(this);mHasCoinState = new HasCoinState(this);mSoldState = new SoldState(this);mWinnerState = new WinnerState(this);if (count > 0) {state = mOnReadyState;} else {state = mSoldOutState;}}// 给糖果机设置状态def setState(state: State) = {this.state = state}def getLocation(): String = {location}def insertCoin() = {state.insertCoin()}def returnCoin() = {state.returnCoin()}def turnCrank() = {state.turnCrank()state.dispense()}def releaseCandy() = {if (count > 0) {count = count - 1println("a candy rolling out!");}}def getCount(): Int = {count}def printstate() = {state.printstate()}def getstate(): State = {state}
}

SoldOutState.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.server// 销售完的状态
class SoldOutState extends State {// 说明:@transient 注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分@transient private var mCandyMachine: CandyMachine = _def this(mCandyMachine: CandyMachine) {thisthis.mCandyMachine = mCandyMachine}override def getstatename(): String = {"SoldOutState"}override def insertCoin(): Unit = {println("you can't insert coin, the machine sold out!")}override def printstate(): Unit = {println("***SoldOutState***")}override def returnCoin(): Unit = {println("you can't return, you haven't inserted a coin yet!")}override def turnCrank(): Unit = {println("you turned, but there are no candies!")}// 没有业务逻辑override def dispense(): Unit = {}
}

OnReadyState.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.serverclass OnReadyState extends State {// 说明:@transient注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分@transient private var mCandyMachine: CandyMachine = _def this(mCandyMachine: CandyMachine) {thisthis.mCandyMachine = mCandyMachine}override def getstatename(): String = {"OnReadyState"}override def insertCoin(): Unit = {println("you have inserted a coin, next, please turn crank!")// 同时给糖果机设置,有硬币的状态this.mCandyMachine.setState(mCandyMachine.mHasCoinState)}override def printstate(): Unit = {println("***OnReadyState***")}override def returnCoin(): Unit = {println("you haven't inserted a coin yet!")}override def turnCrank(): Unit = {println("you turned, but you haven't inserted a coin!")}// 在此状态下 dispense 没有业务逻辑override def dispense(): Unit = {}
}

HasCoinState.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.server// 用户投入了一块硬币后的状态
class HasCoinState extends State {// 说明:@transient 注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分@transient private var mCandyMachine: CandyMachine = _// 观察的是 CandyMachine 糖果机的状态def this(mCandyMachine: CandyMachine) {thisthis.mCandyMachine = mCandyMachine}override def getstatename(): String = {"HasCoinState"}// 根据当前状态,我们的 insertCoin 有不同的业务逻辑override def insertCoin(): Unit = {println("you can't insert another coin!")}override def printstate(): Unit = {println("***HasCoinState***")}override def returnCoin(): Unit = {println("coin return!")// 如果在有 Coin 的状态下,执行了returnCoin,那么糖果机又进入到 redayStatemCandyMachine.setState(mCandyMachine.mOnReadyState)}// 转动手柄override def turnCrank(): Unit = {println("crank turn...!");val ranwinner = new java.util.Random()// 设置一个抽奖随机数,如果返回一个0,就再奖励一块糖val winner = ranwinner.nextInt(10)if (winner == 0) {mCandyMachine.setState(mCandyMachine.mWinnerState)} else {mCandyMachine.setState(mCandyMachine.mSoldState)}}// 没有逻辑override def dispense(): Unit = {}
}

SoldState.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.server// 处于销售的状态
class SoldState extends State {// 说明:@transient 注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分@transient private var mCandyMachine: CandyMachine = _def this(mCandyMachine: CandyMachine) {thisthis.mCandyMachine = mCandyMachine}override def getstatename(): String = {"SoldState"}// 根据当前状态,我们的 insertCoin 有不同的业务逻辑// 其它的方法同样存在这样的处理override def insertCoin(): Unit = {println("please wait! we are giving you a candy!")}override def printstate(): Unit = {println("******SoldState******")}override def returnCoin(): Unit = {println("you haven't inserted a coin yet!")}override def turnCrank(): Unit = {println("we are giving you a candy, turning another get nothing!")}override def dispense(): Unit = {mCandyMachine.releaseCandy() // 数量减去if (mCandyMachine.getCount() > 0) { // 如果还有糖,则进入 readystatemCandyMachine.setState(mCandyMachine.mOnReadyState);} else { // 没有糖,则进入 soleoutstateprintln("Oo, out of candies");mCandyMachine.setState(mCandyMachine.mSoldOutState);}}
}

WinnerState.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.serverclass WinnerState extends State {// 说明:@transient 注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分@transient private var mCandyMachine: CandyMachine = _def this(mCandyMachine: CandyMachine) {thisthis.mCandyMachine = mCandyMachine}override def getstatename(): String = {"WinnerState"}// 根据当前状态,我们的 insertCoin 有不同的业务逻辑override def insertCoin(): Unit = {println("please wait! we are giving you a candy!")}override def printstate(): Unit = {println("***WinnerState***")}override def returnCoin(): Unit = {println("you haven't inserted a coin yet!")}override def turnCrank(): Unit = {println("we are giving you a candy, turning another get nothing!");}override def dispense(): Unit = {mCandyMachine.releaseCandy()if (mCandyMachine.getCount() == 0) {mCandyMachine.setState(mCandyMachine.mSoldOutState);} else {println("you are a winner!you get another candy!")mCandyMachine.releaseCandy()if (mCandyMachine.getCount() > 0) {mCandyMachine.setState(mCandyMachine.mOnReadyState);} else {println("Oo, out of candies");mCandyMachine.setState(mCandyMachine.mSoldOutState);}}}
}

Monitor.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.localimport com.atguigu.chapter17.designpattern.proxy.localcandymachine.server.CandyMachineimport scala.collection.mutable.ListBuffer// 监控糖果机的类
class Monitor {// 可以监控多台糖果机private val candyMachinelst: ListBuffer[CandyMachine] = ListBuffer()// 给监控器增加一个糖果机def addMachine(mCandyMachine: CandyMachine) = {candyMachinelst.append(mCandyMachine)}// 输出该监控器管理的各个糖果机的信息def report() = {// var mCandyMachine:CandyMachine = nullfor (mCandyMachine <- this.candyMachinelst) {println("----------------------------------------")println("Machine Loc:" + mCandyMachine.getLocation())println("Machine Candy count:" + mCandyMachine.getCount())println("Machine State:" + mCandyMachine.getstate().getstatename())}}
}

TestCanyMachine.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.localimport com.atguigu.chapter17.designpattern.proxy.localcandymachine.server.CandyMachine// 测试
object TestCanyMachine {def main(args: Array[String]): Unit = {// 创建一个监控器val mMonitor = new Monitor()// 创建一个 北京-海淀区 的 糖果机,并设置默认有 6 颗糖var mCandyMachine = new CandyMachine("北京-海淀区", 6)// 将该糖果机加入到监控器mMonitor.addMachine(mCandyMachine)mCandyMachine = new CandyMachine("北京-昌平区", 4)// 修改该糖果机的初始状态mCandyMachine.insertCoin() // 投入硬币// 将该糖果机加入到监控器mMonitor.addMachine(mCandyMachine)mCandyMachine = new CandyMachine("北京-朝阳区", 14);// 修改该糖果机的初始状态mCandyMachine.insertCoin() // 投入硬币mCandyMachine.turnCrank() // 转动曲柄,出糖// 将该糖果机加入到监控器mMonitor.addMachine(mCandyMachine)// 输出监控器管理所有糖果机的信息mMonitor.report()}
}

输出结果如下:

you have inserted a coin, next, please turn crank!
you have inserted a coin, next, please turn crank!
crank turn...!
a candy rolling out!
----------------------------------------
Machine Loc:北京-海淀区
Machine Candy count:6
Machine State:OnReadyState
----------------------------------------
Machine Loc:北京-昌平区
Machine Candy count:4
Machine State:HasCoinState
----------------------------------------
Machine Loc:北京-朝阳区
Machine Candy count:13
Machine State:OnReadyState

17.12.4 完成监控远程糖果机

完成监控远程糖果机

远程代理模式监控方案

17.12.6 Java RMI实现远程代理

Java RMI 的介绍(RPC 远程过程调用)
  1、RMI 远程方法调用是计算机之间通过网络实现对象调用的一种通讯机制。
  2、使用 RMI 机制,一台计算机上的对象可以调用另外一台计算机上的对象来获取远程数据。
  3、RMI 被设计成一种面向对象开发方式,允许程序员使用远程对象来实现通信。

Java RMI 的开发应用案例-说明
  请编写一个 JavaRMI 的案例,代理端(客户端)可以通过RMI远程调用远程端注册的一个服务的 sayHello 的方法,并且返回结果。

Java RMI 的开发应用案例-开发步骤
  1、制作远程接口:接口文件
  2、远程接口的实现:Service 文件
  3、RMI 服务端注册,开启服务
  4、RMI 代理端通过 RMI 查询到服务端,建立联系,通过接口调用远程方法

Java RMI 的开发应用案例-程序框架

Java RMI 的开发应用案例-代码实现
代码结构

代码实现
MyRemote.scala  通用接口

package com.atguigu.chapter17.designpattern.proxy.rmiimport java.rmi.{Remote, RemoteException}// 接口文件,该文件会给远程端和本地端使用
trait MyRemote extends Remote {// 一个抽象方法,子类去具体实现@throws(classOf[RemoteException])def sayHello(): String // throws RemoteException
}

MyRemoteClient.scala 客户端找到接口为MyRemote类型到实现类实现调用

package com.atguigu.chapter17.designpattern.proxy.rmiimport java.rmi.Namingclass MyRemoteClient {def go() = {val service = Naming.lookup("rmi://127.0.0.1:9999/RemoteHello").asInstanceOf[MyRemote]val str = service.sayHello()println("str = " + str)}
}object MyRemoteClient {def main(args: Array[String]): Unit = {new MyRemoteClient().go()}
}

MyRemoteImpl.scala 服务器端继承接口注册到服务中

package com.atguigu.chapter17.designpattern.proxy.rmiimport java.rmi.registry.LocateRegistry
import java.rmi.{Naming, RemoteException}
import java.rmi.server.UnicastRemoteObject// 实现了 MyRemote 接口的方法
class MyRemoteImpl extends UnicastRemoteObject with MyRemote {@throws(classOf[RemoteException])override def sayHello(): String = {"Hello World!"}
}// 完成对服务 sayHello 的注册
object MyRemoteImpl {def main(args: Array[String]): Unit = {val service: MyRemote = new MyRemoteImpl()// 把服务绑定到9999端口// 第一种注册方式:// LocateRegistry.createRegistry(9999)// Naming.rebind("RemoteHello", service)// 第二种注册方式:Naming.rebind("rmi://127.0.0.1:9999/RemoteHello", service)println("远程服务开启,在 127.0.0.1 的 9999 端口监听,服务名 RemoteHello")}
}

17.12.7 使用远程代理模式完成远程糖果机监控

类结构图

代码实现
代码结构

大部分代码同上,增加代码如下:
CandyMachineRemote.scala

package com.atguigu.chapter17.designpattern.proxy.remotecandymachine.serverimport java.rmi.{Remote, RemoteException}trait CandyMachineRemote extends Remote {@throws(classOf[RemoteException])def getLocation(): String@throws(classOf[RemoteException])def getCount(): Int@throws(classOf[RemoteException])def getstate(): State
}

RemoteMainTest.scala

package com.atguigu.chapter17.designpattern.proxy.remotecandymachine.serverimport java.rmi.Naming
import java.rmi.registry.LocateRegistryobject RemoteMainTest {def main(args: Array[String]): Unit = {try {val service = new CandyMachine("candymachine1", 10)// LocateRegistry.createRegistry(6602)// Naming.rebind("candymachine1", service)Naming.rebind("rmi://127.0.0.1:6602/candymachine1", service)service.insertCoin() // 故意改变下糖果机的初始状态,看是否在客户端(代理端)监控到service.turnCrank() // 出糖println("服务器端1台糖果机开始运行,在 6602 端口监听...")} catch {case ex: Exception => {ex.printStackTrace()}}}
}

Monitor.scala

package com.atguigu.chapter17.designpattern.proxy.remotecandymachine.clientimport com.atguigu.chapter17.designpattern.proxy.remotecandymachine.server.CandyMachineRemoteimport scala.collection.mutable.ListBufferclass Monitor {// 如果是远程监控,这里我们放的是 CandyMachineRemote,即糖果机的代理private val candyMachinelst: ListBuffer[CandyMachineRemote] = ListBuffer()// 给监控器增加一个糖果机def addMachine(mCandyMachine:CandyMachineRemote) = {candyMachinelst.append(mCandyMachine)}def  report() = {//var mCandyMachine:CandyMachine = nullfor (mCandyMachine <- this.candyMachinelst) {println("----------------------------------------")println("Machine Loc:" + mCandyMachine.getLocation())println("Machine Candy count:" + mCandyMachine.getCount())println("Machine State:" + mCandyMachine.getstate().getstatename())}}
}

TestRemoteMachine.scala

package com.atguigu.chapter17.designpattern.proxy.remotecandymachine.clientimport java.rmi.Namingimport com.atguigu.chapter17.designpattern.proxy.remotecandymachine.server.CandyMachineRemoteobject TestRemoteMachine {def main(args: Array[String]): Unit = {val mMonitor = new Monitor()try {// 通过 rmi 获取到远程的糖果机val mCandyMachine = Naming.lookup("rmi://127.0.0.1:6602/candymachine1").asInstanceOf[CandyMachineRemote]mMonitor.addMachine(mCandyMachine)} catch {case ex: Exception => ex.printStackTrace()}mMonitor.report()}
}

17.12.8 动态代理

动态代理:运行时动态的创建代理类(对象),并将方法调用转发到指定类(对象)。

保护代理:通过前面的分析:大家可以看出动态代理其实就体现出保护代理,即在代理时,对被代理的对象(类)的哪些方法可以调用,哪些方法不能调用在 InvocationHandler 可以控制(通过反射实现)。因此动态代理就体现(实现)了保护代理的效果。

动态代理的应用案例
应用案例说明
  有一个婚恋网项目,女友/男友有个人信息、兴趣爱好和总体评分,要求:
  1、不能自己给自己评分
  2、其它用户可以评分,但是不能设置信息、兴趣爱好。
  3、请使用动态代理实现保护代理的效果。
  4、分析这里我们需要写两个代理。一个是自己使用,一个是提供给其它用户使用。
示意图如下

代码结构

代码实现
PersonBean.scala

package com.atguigu.chapter17.designpattern.proxy.dynamic// 这个就是 Subject
trait PersonBean {def getName(): Stringdef getGender(): Stringdef getInterests(): Stringdef getScore(): Intdef setName(name: String)def setGender(gender: String)def setInterests(interests: String)def setScore(score: Int)
}

PersonBeanImpl.scala

package com.atguigu.chapter17.designpattern.proxy.dynamic// RealSubject 的对象
class PersonBeanImpl extends PersonBean {var name = ""var gender = ""var interests = ""var score: Int = _ // 评分值override def getName(): String = {return name}override def getGender(): String = {gender}override def getInterests(): String = {interests}// 自己可以调用,其他用户不可以调用override def setName(name: String): Unit = {this.name = name}// 自己可以调用,其他用户不可以调用override def setGender(gender: String): Unit = {this.gender = gender}// 自己可以调用,其他用户不可以调用override def setInterests(interests: String): Unit = {this.interests = interests}override def getScore(): Int = {score}// 自己不可以调用,其他用户可以调用override def setScore(score: Int): Unit = {this.score = score}
}

OwnerInvocationHandler.scala

package com.atguigu.chapter17.designpattern.proxy.dynamicimport java.lang.reflect.{InvocationHandler, Method}// 自己调用的代理
class OwnerInvocationHandler extends InvocationHandler {// 设置被调用的对象 PersonBean,实际是 PersonBeanImplvar person: PersonBean = _// 构造器def this(person: PersonBean) {thisthis.person = person}// 说明// 这里的 proxy 就是和 OwnerInvocationHandler 组合的代理@throws(classOf[Throwable])override def invoke(proxy: scala.Any, method: Method, args: Array[AnyRef]): AnyRef = {// 如果是get方法就直接调用if (method.getName().startsWith("get")) {// return method.invoke(person, args) // 此处在 java 中的写法return method.invoke(person) // 此处在 scala 中的写法// 自己不能调用 setHotOrNotRating 方法,即不能给自己评分} else if (method.getName().equals("setScore")) {// 返回一个异常,这里是向上抛出上一级return new IllegalAccessException()// 如果是其他 set 方法就直接调用} else if (method.getName().startsWith("set")) {return method.invoke(person, args(0).toString)}null}
}

NonOwnerInvocationHandler。scala

package com.atguigu.chapter17.designpattern.proxy.dynamicimport java.lang.reflect.{InvocationHandler, Method}// 其它用户调用的代理
class NonOwnerInvocationHandler extends InvocationHandler {// 设置被调用的对象 PersonBean,实际是 PersonBeanImplvar person: PersonBean = _// 构造器def this(person: PersonBean) {thisthis.person = person}// 说明// 这里的 proxy 就是和 NonOwnerInvocationHandler 组合的代理@throws(classOf[Throwable])override def invoke(proxy: scala.Any, method: Method, args: Array[AnyRef]): AnyRef = {// 如果是 get 方法就直接调用if (method.getName().startsWith("get")) {// return method.invoke(person, args) // 此处在 java 中的写法return method.invoke(person) // 此处在 scala 中的写法// 其它用户可以调用 setHotOrNotRating 方法进行评分} else if (method.getName().equals("setScore")) {return method.invoke(person, Integer.valueOf(args(0).toString))// 其它用户不能调用 set 方法} else if (method.getName().startsWith("set")) {return new IllegalAccessException()}null}
}

MatchService.scala

package com.atguigu.chapter17.designpattern.proxy.dynamicimport java.lang.reflect.Proxyclass MatchService {println("****测试OwnerInvocationHandler*************")// 创建一个 Person 对象val tom = getPersonInfo("tom", "男", "爱好编程")// 获取一个给自己调用的代理对象,它替代被调用的对象val OwnerProxy = getOwnerProxy(tom)println("Name is " + OwnerProxy.getName())println("Interests is " + OwnerProxy.getInterests())OwnerProxy.setInterests("爱好淘宝")println("Interests is " + OwnerProxy.getInterests())// 自己给自己设置评分,通过代理控制,不能成功println("Score is " + OwnerProxy.getScore())OwnerProxy.setScore(100) // 给自己刷分println("Score is " + OwnerProxy.getScore()) // 分值仍然为0println("****测试NonOwnerInvocationHandler**********")val mary = getPersonInfo("mary", "女", "爱好购物")val nonOwnerProxy = getNonOwnerProxy(mary)println("Name is " + nonOwnerProxy.getName())println("Interests is " + nonOwnerProxy.getInterests())// 其它人不能修改兴趣,通过代理进行控制不能调用 setInterestsnonOwnerProxy.setInterests("爱好宠物")println("Interests is " + nonOwnerProxy.getInterests())nonOwnerProxy.setScore(68) // 其它人可以评分println("score is " + nonOwnerProxy.getScore())def getPersonInfo(name: String, gender: String, interests: String): PersonBean = {val person = new PersonBeanImpl()person.setName(name)person.setGender(gender)person.setInterests(interests)person}def getOwnerProxy(person: PersonBean): PersonBean = {Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(),new OwnerInvocationHandler(person)).asInstanceOf[PersonBean]}def getNonOwnerProxy(person: PersonBean): PersonBean = {Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(),new NonOwnerInvocationHandler(person)).asInstanceOf[PersonBean]}
}

MainTest

package com.atguigu.chapter17.designpattern.proxy.dynamicobject MainTest {def main(args: Array[String]): Unit = {var mMatchService: MatchService = new MatchService()}
}

输出结果如下:

****测试OwnerInvocationHandler*************
Name is tom
Interests is 爱好编程
Interests is 爱好淘宝
Score is 0
Score is 0
****测试NonOwnerInvocationHandler**********
Name is mary
Interests is 爱好购物
Interests is 爱好购物
score is 68

17.12.9 几种常见的代理模式介绍-几种变体

第十八章 泛型、上下界、视图界定、上下文界定、协变逆变不变

18.1 泛型

泛型的基本介绍
  1、如果我们要求函数的参数可以接受任意类型。可以使用泛型,这个类型可以代表任意的数据类型。 
  2、例如 List,在创建 List 时,可以传入整型、字符串、浮点数等等任意类型。那是因为 List 在 类定义时引用了泛型。比如在 Java 中:public interface List<E> extends Collection<E>

Scala 中泛型应用案例1
要求:
  1、编写一个 Message 类
  2、可以构建 Int 类型的 Message、String 类型的 Message
  3、要求使用泛型来完成设计(说明:不能使用 Any)
示例代码如下:

package com.atguigu.chapter18.genericobject GenericDemo01 {def main(args: Array[String]): Unit = {val mes1 = new IntMessage[Int](20)println(mes1.get)val mes2 = new StrMessage[String]("hello")println(mes2.get)}
}/*
1、编写一个 Message 类
2、可以构建 Int 类型的 Message、String 类型的 Message
3、要求使用泛型来完成设计(说明:不能使用 Any)*/// 在 Scala 定义泛型用[T], t 为泛型的引用
abstract class Message[T](t: T) {def get: T = {t}// 简写形式// def get: T = t
}// 子类扩展的时候,约定了具体的类型
class IntMessage[Int](msg: Int) extends Message(msg)class StrMessage[String](msg: String) extends Message(msg)

输出结果如下:

20
hello

Scala 泛型应用案例2
要求:
  1、请设计一个 EnglishClass(英语班级类),在创建 EnglishClass 的一个实例时,需要指定[班级开班季节(spring,autumn,summer,winter)、班级名称、班级类型]。
  2、开班季节只能是指定的,班级名称为 String,班级类型是(字符串类型 "高级班", "初级班", …) 或者是 Int 类型(1, 2, 3 等)
  3、请使用泛型来完成本案例。
示例代码如下:

package com.atguigu.chapter18.genericimport com.atguigu.chapter18.generic.SeasonEnum.SeasonEnumobject GenericDemo02 {def main(args: Array[String]): Unit = {// 测试val class01 = new EnglishClass[SeasonEnum, String, String](SeasonEnum.spring, "0705", "高级班")println("class01 = " + class01.classSeason +" "+ class01.className +" "+ class01.classType)val class02 = new EnglishClass[SeasonEnum, String, Int](SeasonEnum.spring, "0705", 1)println("class01 = " + class02.classSeason +" "+ class02.className +" "+ class02.classType)}
}/*
1、请设计一个 EnglishClass(英语班级类),在创建 EnglishClass 的一个实例时,需要指定[班级开班季节(spring,autumn,summer,winter)、班级名称、班级类型]。
2、开班季节只能是指定的,班级名称为 String,班级类型是(字符串类型 "高级班", "初级班", ...) 或者是 Int 类型(1, 2, 3 等)
3、请使用泛型来完成本案例。*/
class EnglishClass[A, B, C](val classSeason: A, val className: B, val classType: C)// 季节是枚举类型
object SeasonEnum extends Enumeration {type SeasonEnum = Value // 自定义 SeasonEnum,是 Value 类型,这样才能使用val spring, autumn, summer, winter = Value
}

输出结果如下:

class01 = spring 0705 高级班
class02 = spring 0705 1

Scala 泛型应用案例3
要求:
  1、定义一个函数,可以获取各种类型的 List 的中间 index 的值
  2、使用泛型完成
示例代码如下:

package com.atguigu.chapter18.genericobject GenericDemo03 {def main(args: Array[String]): Unit = {// 测试val list1 = List("hello", "dog", "world")val list2 = List(90, 65, 15)println(midList[String](list1)) // dogprintln(midList[Int](list2)) // 65}/*** 1、定义一个函数,可以获取各种类型的 List 的中间 index 的值* 2、使用泛型完成*/def midList[E](lst: List[E]): E = {lst(lst.length / 2)}
}

输出结果如下:

dog
15

18.2 类型约束-上界(Upper Bounds)/下界(Lower Bounds)

上界(Upper Bounds)介绍和使用

scala 中上界应用案例1
要求:
  1、编写一个通用的类,可以进行Int之间、Float之间 等实现了 Comparable 接口的值直接的比较。
  2、分别使用传统方法和上界的方式来完成,体会上界使用的好处。
示例代码如下:

package com.atguigu.chapter18.upperboundsobject UpperBoundsDemo01 {def main(args: Array[String]): Unit = {// 测试val compareInt = new CompareInt(10, 20)println(compareInt.greater)val compareFloat = new CompareFloat(10.1f, 20.2f)println(compareFloat.greater)// 传统方式扩展性太差,且需要写较多的代码// 上界的第一种用法:val compareCommc1 = new CompareComm(Integer.valueOf(10), Integer.valueOf(20))println(compareCommc1.greater)// 上界的第二种用法:val compareCommc2 = new CompareComm(java.lang.Float.valueOf(15.5f), java.lang.Float.valueOf(25.5f))println(compareCommc2.greater)// 上界的第三种用法:使用了隐式转换,底层隐式函数的妙用:implicit def float2Float(x: Float) = java.lang.Float.valueOf(x) 此乃高级的语法糖val compareCommc3 = new CompareComm[java.lang.Float](35.5f, 45.5f)println(compareCommc3.greater)}
}/*
1、编写一个通用的类,可以进行Int之间、Float之间 等实现了 Comparable 接口的值直接的比较。
2、分别使用传统方式和上界的方式来完成,体会上界使用的好处。*/// 传统的方式
class CompareInt(n1: Int, n2: Int) {// 返回较大的值的方法def greater = if (n1 > n2) n1 else n2
}// 传统的方式
class CompareFloat(n1: Float, n2: Float) {// 返回较大的值的方法def greater = if (n1 > n2) n1 else n2
}// 使用上界的方式,可以有更好的通用性:T <: Comparable[T] 表示 T 类型是 Comparable 的子类型,所以需要你传入的 T 类型必须要实现了 Comparable 接口
class CompareComm[T <: Comparable[T]](obj1: T, obj2: T) {def greater = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

输出结果如下:

20
20.2
20
25.5
45.5

scala 中上界应用案例2
示例代码如下:

package com.atguigu.chapter18.upperboundsobject UpperBoundsDemo02 {def main(args: Array[String]): Unit = {biophony(Seq(new Bird, new Bird)) // ? 没问题,2声鸟叫biophony(Seq(new Animal, new Animal)) // ? 没问题,2声动物叫biophony(Seq(new Animal, new Bird)) // ? 没问题,1声动物叫,1声鸟叫// biophony(Seq(new Earth, new Earth)) // ? 运行报错,因为 Earth 不是 Animal 的子类}// 上界def biophony[T <: Animal](things: Seq[T]) = things map (_.sound)
}class Earth {def sound() {// 方法println("hello earth")}
}class Animal extends Earth {// 重写了 Earth 的方法 sound()override def sound() = {println("animal sound")}
}class Bird extends Animal {// 将 Animal 的方法重写override def sound() = {println("bird sound")}
}

输出结果如下:

bird sound
bird sound
animal sound
animal sound
animal sound
bird sound

下界(Lower Bounds)介绍和使用

scala 中下界应用案例1
示例代码如下:

package com.atguigu.chapter18.lowerBoundsobject LowerBoundsDemo01 {def main(args: Array[String]): Unit = {biophony(Seq(new Earth, new Earth)).map(_.sound()) // ? 没问题,2声hello earthbiophony(Seq(new Animal, new Animal)).map(_.sound()) // ? 没问题,2声动物叫biophony(Seq(new Bird, new Bird)).map(_.sound()) // ? 没问题,2声鸟叫,很怪!/*Scala 把它看做了 Object。也就是说,可以随便传!1)只不过和 Animal 直系的,是 Animal 父类的还是按照父类处理,是 Animal 子类的按照 Animal 处理。2)和 Animal 无关的,一律按照 Object 处理!*/}// 下界def biophony[T >: Animal](things: Seq[T]) = things
}class Earth {def sound() {// 方法println("hello earth")}
}class Animal extends Earth {// 重写了 Earth 的方法 sound()override def sound() = {println("animal sound")}
}class Bird extends Animal {// 将 Animal 的方法重写override def sound() = {println("bird sound")}
}class Moon {}

输出结果如下:

hello earth
hello earth
animal sound
animal sound
bird sound
bird sound

scala 中下界的使用小结:PPT重点

查看类型截图

18.3 类型约束-视图界定(View Bounds)

视图界定基本介绍

视图界定应用案例1
示例代码如下:

package com.atguigu.chapter18.viewboundsobject ViewBoundsDemo01 {def main(args: Array[String]): Unit = {// 方式1:使用了隐式转换val compareComm1 = new CompareComm(20, 30)println(compareComm1.greater)// 同时,也支持前面学习过的上界使用的各种方式,看后面代码val compareComm2 = new CompareComm(Integer.valueOf(20), Integer.valueOf(30))println(compareComm2.greater)val compareComm3 = new CompareComm[java.lang.Float](201.9f, 310.1f)println(compareComm3.greater)// 上面的小数比较,在视图界定的情况下,就可以这样写了val compareComm5 = new CompareComm(201.9f, 310.1f)println(compareComm5.greater)}
}// <% 视图界定 view bounds 不仅支持上界使用的各种方式,还会发生隐式转换
class CompareComm[T <% Comparable[T]](obj1: T, obj2: T) {def greater = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

输出结果如下:

30
30
310.1
310.1

视图界定应用案例2
使用视图界定的方式:
比较两个 Person 对象的年龄大小。
比较两个 Cat 对象的名字长度大小
示例代码如下:

package com.atguigu.chapter18.viewboundsobject ViewBoundsDemo02 {def main(args: Array[String]): Unit = {val p1 = new Person("tom", 10)val p2 = new Person("jack", 20)val compareComm2 = new CompareComm2(p1, p2)println(compareComm2.getter)val c1 = new Cat("tom")val c2 = new Cat("test")val compareComm3 = new CompareComm2(c1, c2)println(compareComm3.getter.name)}
}// 比较两个 Person 对象的年龄大小
class Person(val name: String, val age: Int) extends Ordered[Person] {// 重写了 Ordered 的 compare 方法override def compare(that: Person): Int = this.age - that.age// 重写了 Ordered 的 toString 方法,是为了显示方便override def toString: String = this.name + "\t" + this.age
}// 比较两个 Cat 对象的名字长度大小
class Cat(val name: String) extends Ordered[Cat] {// 重写了 Ordered 的 compare 方法override def compare(that: Cat): Int = this.name.length - that.name.length
}// 1、T <% Ordered[T] 表示 T 是 Ordered 的子类型
// 2、CompareComm2 中调用的 compareTo 方法是 T 这个类型的方法
class CompareComm2[T <% Ordered[T]](obj1: T, obj2: T) {def getter = if (obj1 > obj2) obj1 else obj2def getter2 = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

输出结果如下:

jack    20
test

视图界定应用案例3
自己写隐式转换结合视图界定的方式,比较两个 Person 对象的年龄大小。
示例代码如下:
MyImplicit.scala

package com.atguigu.chapter18.viewboundsobject MyImplicit {implicit def person22OrderedPerson2(person: Person2) = new Ordered[Person2] {override def compare(that: Person2): Int = person.age - that.age // 自己的业务逻辑}
}

ViewBoundsDemo03.scala

package com.atguigu.chapter18.viewboundsobject ViewBoundsDemo03 {def main(args: Array[String]): Unit = {val p1 = new Person2("tom", 110)val p2 = new Person2("jack", 20)// 引入隐式函数import MyImplicit._val compareComm3 = new CompareComm3(p1, p2)println(compareComm3.geater)}
}class Person2(val name: String, val age: Int) {override def toString = this.name + "\t" + this.age
}class CompareComm3[T <% Ordered[T]](obj1: T, obj2: T) {def geater = if (obj1 > obj2) obj1 else obj2
}

输出结果如下:

tom    110

18.4 类型约束-上下文界定(Context Bounds)

基本介绍
  与 View Bounds 一样 Context Bounds(上下文界定) 也是隐式参数的语法糖。为语法上的方便,引入了 “上下文界定” 这个概念。

上下文界定应用实例
要求:使用上下文界定+隐式参数方式,比较两个 Person 对象的年龄大小
要求:使用 Ordering 实现比较
示例代码如下:

package com.atguigu.chapter18.contextboundsobject ContextBoundsDemo01 {// 定义一个隐式值,类型是 Ordering[Person]implicit val personComparetor = new Ordering[Person] {override def compare(p1: Person, p2: Person): Int =p1.age - p2.age}def main(args: Array[String]): Unit = {val p1 = new Person("mary", 30)val p2 = new Person("smith", 35)val compareComm4 = new CompareComm4(p1, p2)println(compareComm4.geatter)val compareComm5 = new CompareComm5(p1, p2)println(compareComm5.geatter)val compareComm6 = new CompareComm6(p1, p2)println(compareComm6.geatter)}
}// 方式1
// 1、obj1: T, obj2: T 表示 T 类型的对象
// 2、implicit comparetor: Ordering[T] 是一个隐式参数
class CompareComm4[T: Ordering](obj1: T, obj2: T)(implicit comparetor: Ordering[T]) {def geatter = if (comparetor.compare(obj1, obj2) > 0) obj1 else obj2
}// 方式2:将隐式参数放到方法内
class CompareComm5[T: Ordering](o1: T, o2: T) {def geatter = {def f1(implicit comparetor: Ordering[T]) = comparetor.compare(o1, o2)if (f1 > 0) o1 else o2}
}// 方式3:使用 implicitly 语法糖,最简单(推荐使用)
class CompareComm6[T: Ordering](o1: T, o2: T) {def geatter = {// 这句话就会发生隐式转换,获取到隐式值 personComparetorval comparetor = implicitly[Ordering[T]] // 底层使用编译器来完成绑定工作println("CompareComm6 comparetor " + comparetor.hashCode())if (comparetor.compare(o1, o2) > 0) o1 else o2}
}// 一个普通的 Person 类
class Person(val name: String, val age: Int) {override def toString = this.name + "\t" + this.age
}

输出结果如下:

smith    35
smith    35
CompareComm6 comparetor 1513712028
smith    35

Ordered 和 Ordering的区别
  Ordering 继承了 java 中的 Comparator 接口,而 Ordered 继承了 java 的 Comparable 接口。 
  而在 java 中的 Comparator 是一个外部比较器,需要定义一个类来实现比较器;而 Comparable 则是一个内部比较器,在类内部重写 compareTo 函数。

18.5 协变、逆变和不变

基本介绍

应用实例
示例代码如下:

package com.atguigu.chapter18.covariantobject Demo {def main(args: Array[String]): Unit = {val t1: Temp3[Sub] = new Temp3[Sub]("hello world") // ok// val t2: Temp3[Super] = new Temp3[Sub]("hello world")// val t3: Temp3[Sub] = new Temp3[Super]("hello world")val t11: Temp4[Sub] = new Temp4[Sub]("hello world") // okval t12: Temp4[Super] = new Temp4[Sub]("hello world") // ok// val t13: Temp4[Sub] = new Temp4[Super]("hello world")val t111: Temp5[Sub] = new Temp5[Sub]("hello world") // ok// val t112: Temp5[Super] = new Temp5[Sub]("hello world")val t113: Temp5[Sub] = new Temp5[Super]("hello world") // ok}
}// 不变
class Temp3[A](title: String) {override def toString: String = {title}
}// 协变
class Temp4[+A](title: String) {override def toString: String = {title}
}// 逆变
class Temp5[-A](title: String) {override def toString: String = {title}
}class Superclass Sub extends Super

12:设计模式、泛型、上下界、视图界定、上下文界定、协变逆变不变相关推荐

  1. 大数据技术之_16_Scala学习_12_设计模式+泛型、上下界、视图界定、上下文界定、协变逆变不变

    大数据技术之_16_Scala学习_12 第十七章 设计模式 17.1 学习设计模式的必要性 17.2 掌握设计模式的层次 17.3 设计模式的介绍 17.4 设计模式的类型 17.5 简单工厂模式( ...

  2. scala泛型上下界

    // 如果既需要上界,又需要下界,那么下界在前,上界在后 object D03_泛型上下界 {class Personclass PoliceMen extends Personclass Super ...

  3. 泛型型协变逆变_Java泛型类型简介:协变和逆变

    泛型型协变逆变 by Fabian Terh 由Fabian Terh Java泛型类型简介:协变和逆变 (An introduction to generic types in Java: cova ...

  4. 10天学会kotlin DAY7 接口 泛型 协变 逆变

    kotlin 接口 泛型 协变 逆变 前言 1.接口的定义 2.抽象类 3.定义泛型类 4.泛型函数 5.泛型变换 6.泛型类型约束 7.vararg 关键字(动态参数) 8.[] 操作符 9.out ...

  5. Scala语言学习笔记——泛型、上下界、视图界定、上下文界定、协变逆变不变、闭包、柯里化

    1.Scala泛型 应用案例1 /*** @author huleikai* @create 2019-05-27 11:23*/ object TestFanXing {def main(args: ...

  6. java 上界和下界,Java 泛型上下界(上下限)

    UML 类图,A 继承自 Object,B 继承自 A,C 继承自 B public class Test_2 { public static void main(String[] args) { L ...

  7. 上界与下界-- 视图界定--协变与逆变

    1.上界与下界:Upper bound,lower bound                  作用:规定泛型的取值范围                  Int x                 ...

  8. C#高级语法之泛型、泛型约束,类型安全、逆变和协变(思想原理)

    一.为什么使用泛型? 泛型其实就是一个不确定的类型,可以用在类和方法上,泛型在声明期间没有明确的定义类型,编译完成之后会生成一个占位符,只有在调用者调用时,传入指定的类型,才会用确切的类型将占位符替换 ...

  9. Java 泛型的不变性 (invariance)、协变性 (covariance)、逆变性 (contravariance)

    本文整理自:https://chiclaim.blog.csdn.net/article/details/85575213 我们先定义三个类:Plate.Food.Fruit //定义一个`盘子`类 ...

最新文章

  1. mysql中如何设置过滤器_mysql – 使用计数器实现过滤器
  2. 了解下SOAP Header 元素
  3. 模板 - 最小树形图(朱刘算法)
  4. react把表格渲染好ui_在React中实现条件渲染的7种方法
  5. Python学习之路 (一)开发环境搭建
  6. Sublime text3关闭自动更新(hosts屏蔽)
  7. 小米3g刷高格固件_小米路由器3G刷了老毛子之后恢复官方固件
  8. MATLAB元胞自动机报告,元胞自动机概述与MATLAB实现
  9. jinja Escaping
  10. JavaScript学习总结(15)——十大经典排序算法的JS版
  11. C#多线程强制退出程序
  12. java final 变量 大小写_java – 为什么“final static int”可以用作开关的大小写常量但不是“final static”...
  13. Markdown - 语法简介
  14. cadence17.4 设计PCB零基础教程入门学习过程
  15. Maya2018生成pyd文件
  16. 论文写作中文核心期刊查询和中图检索号查询
  17. 夜神模拟器如何设置自动代理
  18. papervision3d 开发经典例子
  19. 2022年12月最新微博新版批量删除微博博文代码_删除清空微博博文的微博批量删除代码与方法
  20. android 手机桌面角标,华为手机设置桌面图标角标提醒的实现

热门文章

  1. ChatExcel 来了,太酷炫了!
  2. python 封装API接口
  3. busybox 知:构建
  4. 手机架服务器性能,手机架云服务器
  5. [思索] 我追捧的偶像 -- 王垠
  6. EOF和NULL的区别
  7. 用一个例子来解释ThreadLocal
  8. 记考勤功能的简单实现
  9. windows python2和python3共存_windows python2与python3环境共存简易方法
  10. 2016年学习JavaScript是怎样的一种体验