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

第十七章 设计模式17.1 学习设计模式的必要性17.2 掌握设计模式的层次17.3 设计模式的介绍17.4 设计模式的类型17.5 简单工厂模式(生产类)17.5.1 基本介绍17.5.2 看一个具体的需求17.5.3 使用传统的方式来完成17.5.4 使用简单工厂模式来完成17.6 工厂方法模式(生产方法)17.6.1 看一个新的需求17.6.2 工厂方法模式介绍17.6.3 工厂方法模式应用案例17.7 抽象工厂模式(生产类)17.7.1 基本介绍17.7.3 抽象工厂模式应用案例17.8 工厂模式小结17.9 单例模式17.9.1 什么是单例模式+单例模式的应用场景17.9.2 单例模式的应用案例-懒汉式17.9.2 单例模式的应用案例-饿汉式17.10 装饰者模式(Decorator)17.10.1 看一个具体的需求17.10.2 方案1-较差的方案+小结和分析17.10.3 方案2-好点的方案+小结和分析17.10.4 装饰者模式原理17.10.5 装饰者模式定义17.10.6 用装饰者模式设计重新设计的方案17.10.7 装饰者模式咖啡订单项目应用实例17.10.8 Java 中装饰者模式的经典使用17.11 观察者模式(Observer)17.11.1 看一个具体的需求17.11.2 气象站设计方案1-普通方案17.11.3 观察者模式原理17.11.4 气象站设计方案2-观察者模式17.11.5 Java 内置观察者模式17.12 代理模式(Proxy)17.12.1 代码模式的基本介绍17.12.2 看一个具体的需求17.12.3 完成监控本地糖果机17.12.4 完成监控远程糖果机17.12.6 Java RMI实现远程代理17.12.7 使用远程代理模式完成远程糖果机监控17.12.8 动态代理17.12.9 几种常见的代理模式介绍-几种变体第十八章 泛型、上下界、视图界定、上下文界定、协变逆变不变18.1 泛型18.2 类型约束-上界(Upper Bounds)/下界(Lower Bounds)18.3 类型约束-视图界定(View Bounds)18.4 类型约束-上下文界定(Context Bounds)18.5 协变、逆变和不变


经典的 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).reverse
    println("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.user

import com.atguigu.chapter17.designpattern.simplefactory.pizzastore.pizza.{CheesePizza, GreekPizza, PepperPizza, Pizza}

object SimplePizzaFactory {
  // 提供一个创建 Pizza 的方法
  def createPizza(t: String): Pizza = {
    var pizza: Pizza = null
    if (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.user

import com.atguigu.chapter17.designpattern.simplefactory.pizzastore.pizza.{Pizza}

import scala.util.control.Breaks._
import scala.io.StdIn

class 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


图解2

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

17.7.1 基本介绍

17.7.3 抽象工厂模式应用案例

图解1


图解2

17.8 工厂模式小结


图解

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

17.9 单例模式

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

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

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

package com.atguigu.chapter17.designpattern.singleton

object TestSingleTon1 extends App {
  val singleTon1 = SingleTon1.getInstance
  val singleTon2 = SingleTon1.getInstance
  println(singleTon1.hashCode() + " " + singleTon2.hashCode())
}

// 将 SingleTon1 的构造方法私有化
class SingleTon1 private() 
{
  println("~")
}

object SingleTon1 {
  private var s: SingleTon1 = null

  def 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.singleton

object TestSingleTon2 extends App {
  val singleTon1 = SingleTon2.getInstance
  val singleTon2 = SingleTon2.getInstance
  println(singleTon1.hashCode() + " " + singleTon2.hashCode())
}

// 将 SingleTon2 的构造方法私有化
class SingleTon2 private() 
{
  println("~~")
}

object SingleTon2 {
  private val s: SingleTon2 = new SingleTon2

  def 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) {
    this
    this.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 = mTemperature
    this.mPressure = mPressure
    this.mHumidity = mHumidity
    dataChange()  // 再去修改天气公告板最新数据
  }
}

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 = mTemperature
    this.mPressure = mPressure
    this.mHumidity = mHumidity
    display() // 显示操作
  }

  // 显示方法
  def display() 
= {
    println("***气象局的天气公告板显示 Today mTemperature: " + mTemperature + "***")
    println("***气象局的天气公告板显示 Today mPressure: " + mPressure + "***")
    println("***气象局的天气公告板显示 Today mHumidity: " + mHumidity + "***")
  }
}

InternetWeather.scala

package com.atguigu.chapter17.designpattern.observer.localinternetobserver

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

输出结果如下:

***气象局的天气公告板显示 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.mode

import 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 = mTemperature
    this.mPressure = mPressure
    this.mHumidity = mHumidity
    dataChange()
  }

  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.mode

import 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 = mTemperature
    this.mPressure = mPressure
    this.mHumidity = mHumidity
    display() // 显示操作
  }

  // 显示方法
  def display() 
= {
    println("***Today mTemperature: " + mTemperature + "***")
    println("***Today mPressure: " + mPressure + "***")
    println("***Today mHumidity: " + mHumidity + "***")
  }
}

ForcastConditions.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.mode

import 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 = mTemperatrue
    this.mPressure = mPressure
    this.mHumidity = mHumidity
    display()
  }

  def display() 
= {
    println("***广播播报明天温度:" + mTemperature  + "***")
    println("***广播播报明天气压:" + mPressure + "***")
    println("***广播播报明天湿度:" + mHumidity + "***")
  }
}

InternetWeather.scala

package com.atguigu.chapter17.designpattern.observer.internetobserver.mode

object 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(3015040)

    // 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.server

trait 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 = 0

  def this(location: String, count: Int) {
    this
    this.location = location
    this.count = count
    mSoldOutState = 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 - 1
      println("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) 
{
    this
    this.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.server

class OnReadyState extends State {
  // 说明:@transient注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分
  @transient private var mCandyMachine: CandyMachine = _

  def this(mCandyMachine: CandyMachine) 
{
    this
    this.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) {
    this
    this.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,那么糖果机又进入到 redayState
    mCandyMachine.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) 
{
    this
    this.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) { // 如果还有糖,则进入 readystate
      mCandyMachine.setState(mCandyMachine.mOnReadyState);
    } else { // 没有糖,则进入 soleoutstate
      println("Oo, out of candies");
      mCandyMachine.setState(mCandyMachine.mSoldOutState);
    }
  }
}

WinnerState.scala

package com.atguigu.chapter17.designpattern.proxy.localcandymachine.server

class WinnerState extends State {
  // 说明:@transient 注解将字段标记为瞬态的,即表示一个域不是该对象串行化的一部分
  @transient private var mCandyMachine: CandyMachine = _

  def this(mCandyMachine: CandyMachine) 
{
    this
    this.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.local

import com.atguigu.chapter17.designpattern.proxy.localcandymachine.server.CandyMachine

import scala.collection.mutable.ListBuffer

// 监控糖果机的类
class Monitor {

  // 可以监控多台糖果机
  private val candyMachinelst: ListBuffer[CandyMachine] = ListBuffer()

  // 给监控器增加一个糖果机
  def addMachine(mCandyMachine: CandyMachine) = {
    candyMachinelst.append(mCandyMachine)
  }

  // 输出该监控器管理的各个糖果机的信息
  def report() = {
    // var mCandyMachine:CandyMachine = null
    for (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.local

import 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 的介绍
  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.rmi

import java.rmi.{Remote, RemoteException}

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

MyRemoteClient.scala

package com.atguigu.chapter17.designpattern.proxy.rmi

import java.rmi.Naming

class 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.rmi

import 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.server

import 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.server

import java.rmi.Naming
import java.rmi.registry.LocateRegistry

object 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.client

import com.atguigu.chapter17.designpattern.proxy.remotecandymachine.server.CandyMachineRemote

import scala.collection.mutable.ListBuffer

class Monitor {

  // 如果是远程监控,这里我们放的是 CandyMachineRemote,即糖果机的代理
  private val candyMachinelst: ListBuffer[CandyMachineRemote] = ListBuffer()

  // 给监控器增加一个糖果机
  def addMachine(mCandyMachine:CandyMachineRemote) = {
    candyMachinelst.append(mCandyMachine)
  }

  def  report() = {
    //var mCandyMachine:CandyMachine = null
    for (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.client

import java.rmi.Naming

import com.atguigu.chapter17.designpattern.proxy.remotecandymachine.server.CandyMachineRemote

object 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(): String

  def getGender(): String

  def getInterests(): String

  def getScore(): Int

  def 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.dynamic

import java.lang.reflect.{InvocationHandler, Method}

// 自己调用的代理
class OwnerInvocationHandler extends InvocationHandler {
  // 设置被调用的对象 PersonBean,实际是 PersonBeanImpl
  var person: PersonBean = _

  // 构造器
  def this(person: PersonBean) {
    this
    this.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.dynamic

import java.lang.reflect.{InvocationHandler, Method}

// 其它用户调用的代理
class NonOwnerInvocationHandler extends InvocationHandler {
  // 设置被调用的对象 PersonBean,实际是 PersonBeanImpl
  var person: PersonBean = _

  // 构造器
  def this(person: PersonBean) {
    this
    this.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.dynamic

import java.lang.reflect.Proxy

class 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()) // 分值仍然为0


  println("****测试NonOwnerInvocationHandler**********")
  val mary = getPersonInfo("mary""女""爱好购物")
  val nonOwnerProxy = getNonOwnerProxy(mary)
  println("Name is " + nonOwnerProxy.getName())
  println("Interests is " + nonOwnerProxy.getInterests())

  // 其它人不能修改兴趣,通过代理进行控制不能调用 setInterests
  nonOwnerProxy.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.dynamic

object 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.generic

object 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](tT{
  def get: T = {
    t
  }

  // 简写形式
  // def get: T = t
}

// 子类扩展的时候,约定了具体的类型
class IntMessage[Int](msgIntextends Message(msg)

class StrMessage[String](msgStringextends Message(msg)

输出结果如下:

20
hello

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

package com.atguigu.chapter18.generic

import com.atguigu.chapter18.generic.SeasonEnum.SeasonEnum

object 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[ABC](val classSeasonAval classNameBval classTypeC)

// 季节是枚举类型
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.generic

object GenericDemo03 {
  def main(args: Array[String]): Unit = {
    // 测试
    val list1 = List("hello""dog""world")
    val list2 = List(906515)

    println(midList[String](list1)) // dog
    println(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.upperbounds

object UpperBoundsDemo01 {
  def main(args: Array[String]): Unit = {
    // 测试
    val compareInt = new CompareInt(1020)
    println(compareInt.greater)
    val compareFloat = new CompareFloat(10.1f20.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.5f45.5f)
    println(compareCommc3.greater)
  }
}

/*
1、编写一个通用的类,可以进行Int之间、Float之间 等实现了 Comparable 接口的值直接的比较。
2、分别使用传统方式和上界的方式来完成,体会上界使用的好处。
 */


// 传统的方式
class CompareInt(n1Intn2Int{
  // 返回较大的值的方法
  def greater = if (n1 > n2) n1 else n2
}

// 传统的方式
class CompareFloat(n1Floatn2Float{
  // 返回较大的值的方法
  def greater = if (n1 > n2) n1 else n2
}

// 使用上界的方式,可以有更好的通用性:T <: Comparable[T] 表示 T 类型是 Comparable 的子类型,所以需要你传入的 T 类型必须要实现了 Comparable 接口
class CompareComm[T <: Comparable[T]](obj1Tobj2T{
  def greater = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

输出结果如下:

20
20.2
20
25.5
45.5

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

package com.atguigu.chapter18.upperbounds

object 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.lowerBounds

object LowerBoundsDemo01 {
  def main(args: Array[String]): Unit = {
    biophony(Seq(new Earth, new Earth)).map(_.sound()) // ? 没问题,2声hello earth
    biophony(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 中下界的使用小结


查看类型截图

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

视图界定基本介绍

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

package com.atguigu.chapter18.viewbounds

object ViewBoundsDemo01 {
  def main(args: Array[String]): Unit = {
    // 方式1:使用了隐式转换
    val compareComm1 = new CompareComm(2030)
    println(compareComm1.greater)
    // 同时,也支持前面学习过的上界使用的各种方式,看后面代码
    val compareComm2 = new CompareComm(Integer.valueOf(20), Integer.valueOf(30))
    println(compareComm2.greater)

    val compareComm3 = new CompareComm[java.lang.Float](201.9f310.1f)
    println(compareComm3.greater)
    // 上面的小数比较,在视图界定的情况下,就可以这样写了
    val compareComm5 = new CompareComm(201.9f310.1f)
    println(compareComm5.greater)
  }
}

// <% 视图界定 view bounds 不仅支持上界使用的各种方式,还会发生隐式转换
class CompareComm[T <% Comparable[T]](obj1Tobj2T{
  def greater = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

输出结果如下:

30
30
310.1
310.1

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

package com.atguigu.chapter18.viewbounds

object 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 nameStringval ageIntextends 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 nameStringextends 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]](obj1Tobj2T{
  def getter = if (obj1 > obj2) obj1 else obj2

  def getter2 = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

输出结果如下:

jack    20
test

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

package com.atguigu.chapter18.viewbounds

object 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.viewbounds

object 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 nameStringval ageInt{
  override def toString = this.name + "\t" + this.age
}

class CompareComm3[T <% Ordered[T]](obj1Tobj2T{
  def geater = if (obj1 > obj2) obj1 else obj2
}

输出结果如下:

tom    110

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

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

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

package com.atguigu.chapter18.contextbounds

object 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[TOrdering](obj1Tobj2T)(implicit comparetorOrdering[T]) {
  def geatter = if (comparetor.compare(obj1, obj2) > 0) obj1 else obj2
}

// 方式2:将隐式参数放到方法内
class CompareComm5[TOrdering](o1To2T{
  def geatter = {
    def f1(implicit comparetor: Ordering[T]) = comparetor.compare(o1, o2)

    if (f1 > 0) o1 else o2
  }
}

// 方式3:使用 implicitly 语法糖,最简单(推荐使用)
class CompareComm6[TOrdering](o1To2T{
  def geatter = {
    // 这句话就会发生隐式转换,获取到隐式值 personComparetor
    val comparetor = implicitly[Ordering[T]] // 底层使用编译器来完成绑定工作
    println("CompareComm6 comparetor " + comparetor.hashCode())
    if (comparetor.compare(o1, o2) > 0) o1 else o2
  }
}

// 一个普通的 Person 类
class Person(val nameStringval ageInt{
  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.covariant

object 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"// ok
    val 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](titleString{
  override def toString: String = {
    title
  }
}

// 协变
class Temp4[+A](titleString{
  override def toString: String = {
    title
  }
}

// 逆变
class Temp5[-A](titleString{
  override def toString: String = {
    title
  }
}

class Super

class Sub extends Super
posted @ 2019-04-07 21:03  黑泽君  阅读(555)  评论(0编辑  收藏  举报