美文网首页
scala-隐式机制及Akka

scala-隐式机制及Akka

作者: 奋斗的蛐蛐 | 来源:发表于2021-03-15 10:18 被阅读0次

隐式机制及Akka

隐式转换

隐式转换和隐式参数时Scala中两个非常强大的功能,利用隐式转换和隐式参数,可以提供类库,对类库的使用者隐匿掉具体细节。

Scala会根据隐式转化函数的签名,在程序中使用隐式转换函数接收的参数类型定义的对象时,会自动将其传入隐式转换函数,转换为另一种类型的对象并返回,这就是隐式转换

  • 首先有一个隐式转换函数
  • 使用到隐式转换函数接收的参数类型定义的对象
  • Scala自动传入隐式转换函数,并完成对象的类型转换

隐式转换需要使用implicit关键字。

使用Scala隐式转化有一定的限制:

  • implicit关键字只能用来修饰方法、变量、参数
  • 隐式转换的函数只在当前范围内才有效。如果隐式转换不在当前范围内定义,那么必须通过import语句将其导入

Spark源码中有大量的隐式转换和隐式参数,因此必须掌握

隐式转换函数

Scala的隐式转换最核心的就是定义隐式转换函数,即 implicit conversion function。

定义隐式转换函数,只要在编写程序内引入,就会被Scala自动使用。

隐式转换函数有Scala自动调用,通常建议将隐式函数命名问one2one的形式

示例一:

package hhb.cn.part10

class Num{}

class RichNum(num: Num) {
  def rich(): Unit = {
    println("============")
  }
}

/**
 * 自定义了一个伴生对象,生成一个apply方法
 */
object RichNum {
  def apply(num: Num): RichNum = {
    new RichNum(num)
  }
}

object ImplicitDemo {
  //定义一个隐式转换函数,命名要符合one2one的命名格式
  implicit def num2RichNum(num: Num): RichNum = {
    RichNum(num)
  }


  def main(args: Array[String]): Unit = {

    val num = new Num
    //num类型并没有rich方法,但是Scala编译器会查找当前范围内的隐式转换函数,
    //然后将其转换成RichNum类型,最终调用rich方法
    num.rich()
  }
}

示例二:导入隐式函数

package hhb.cn.part10

object IntToStringImplicit {

  implicit def int2String(num: Int): String = {
    num.toString
  }
}

下面代码中调用了String类型的length方法,Int类型本身没有length方法,但是在可用范围内定义了可以把Int转换为String的隐式函数int2String,因此函数编译通过并运行出正确的结果。

此示例中隐式函数的定义必须定义在使用之前,否则编译报错。

package hhb.cn.part10

import hhb.cn.part10.IntToStringImplicit.int2String

object ImplicitDemoTwo {

  def main(args: Array[String]): Unit = {
    val num = 10
    println(num.length)
  }
}

通过hhb.cn.part10.IntToStringImplicit.int2String,将Int2StringTest内部的成员导入到相应的作用域内,否则无法调用隐式函数。

要实现隐式转换,只要在程序可见的范围内定义隐式转换函数即可,Scala会自动使用隐式转换函数。隐式转换函数与普通函数的语法区别就是,要以implicit开头,而且最好要定义函数返回类型。

隐式转换案例:特殊售票窗口(只接受特殊人群买票,比如学生、老人等),其他人不能在特殊售票窗口买票。

package hhb.cn.part10


class SpecialPerson(var name: String)

class Older(var name: String)

class Worker(var name: String)

class Student(var name: String)

object ImplicitDemoThree {


  def SpecialBuyTick(specialPerson: SpecialPerson) = {
    if (specialPerson != null)
      println(specialPerson.name + " 购买了特殊票")
    else
      println("不能买")
  }

  //定义一个隐式转换函数
  implicit def anyToSpecial(any: Any): SpecialPerson = {
    any match {
      case any: Older => new SpecialPerson(any.asInstanceOf[Older].name)
      case any: Student => new SpecialPerson(any.asInstanceOf[Student].name)
      case _ => null
    }
  }

  def main(args: Array[String]): Unit = {
    val older = new Older("older")
    val student = new Student("student")
    val worker = new Worker("worker")

    SpecialBuyTick(older)
    SpecialBuyTick(student)
    SpecialBuyTick(worker)
  }
}

隐式参数和隐式值

在函数定义的时候,支持在最后一组参数中使用implicit,表明这是一组隐式参数,在调用该函数的时候,可以不用传递隐式参数,而编译器会自动寻找一个 implicit 标记过的合适的值作为参数

Scala编译器会在两个范围内查找:

  • 当前作用域内可见的val或者var定义隐式变量
  • 隐式参数类型的伴生对象内隐式值
object Doubly {
  //在print函数中定义一个隐式参数fmt
  def print(num: Double)(implicit fmt: String): Unit = {
    println(fmt format (num))
  }
  def main(args: Array[String]): Unit = {
    //此时调用print函数需要为第二个隐式参数赋值
    print(3.12)("%.1f")

    //定义一个隐式变量
    implicit val printFmt="%.3f"
    //当调用print函数时没有给第二个隐式参数赋值,
    //那么Scala会在当前作用域内寻找可见的val或var定义的隐式变量,一旦找到就会应用
    print(3.12)
  }
}

类型参数

Scala类型参数与Java的泛型是一样的,可以在集合、类、函数中定义类型参数,从而保证程序更好的健壮性。

泛型类

泛型类,顾名思义,其实就是在类的声明中定义一些泛型类型,然后在类内部的字段或方法。就可以使用这些泛型类型。

使用泛型类,通常是需要对类中的某些成员变量,比如某个字段和方法中的参数或变量进行统一的类型限制,这样可以保证程序更好的健壮性和稳定性。

如果不使用泛型进行统一的类型限制,那么在后期程序运行过程中难免会出现问题,比如传入了不希望的类型导致程序出问题。

在使用泛型类的时候,比如创建泛型类的对象,只需将类型参数替换成时间的畸形即可。

Scala自动推断泛型类型特效,直接给使用泛型的字段赋值时,Scala会自动进行类型推断

泛型类的定义如下:

//定义一个泛型类
class Spark[T1, T2, T3](n: T1) {
  var name: T1 = n
  var age: T2 = _
  var address: T3 = _

  def getInfo(): Unit = {
    println(s"$name,$age,$address")
  }
}

使用上述的泛型类,只需要使用具体的类型代替类型参数即可。

object GenericDemo extends App {

  val spark = new Spark[String, Int, String]("zhangsan")

  spark.getInfo() //zhangsan,null,null
  spark.age = 20
  spark.address = "123"
  spark.getInfo() //zhangsan,20,123
}

泛型函数

泛型函数,与泛型类类似,可以给某个函数在声明时指定泛型类型,然后在函数体内,多个变量或者返回值之间,就可以使用泛型类型进行声明,从而对某个特殊的变量,或者多个变量,进行强制性的类型限制。

与泛型类一样,你可以通过给使用了泛型类型的变量传递值来让Scala自动推断泛型的实际类型,也可以在调用函数时,手动指定泛型类型。

案例:卡片售卖机,可以指定卡片的内容,内容可以是String类型或Int类型

object GenericFunc {

  def getCart[T](context: T): Unit = {
    context match {
      case context: Int => s"cart : $context is Int"
      case context: String => s"cart : $context is String"
      case _ => "other"
    }
  }

  def main(args: Array[String]): Unit = {
    println(getCart[String]("12312313"))
    println(getCart(123))
    println(getCart(123.0))

  }

}

协变和逆变

Scala的协变和逆变是非常有特色的,完全解决的Java中泛型的一大缺憾

举例来说,Java中,如果Process是Master的子类,那么Cart[Process]却不是Card[Master]的子类。而Scala中,只要灵活的使用协变和逆变就可以解决Java泛型的问题。

协变定义形式如:trait List[+T]{}

当类型s是类型A的子类型时,则List[s]也可以认为是List[A]的子类,即List[S]可以泛化List[A],也就是被参数化,类型的泛化方向与参数类型一致,所以被称为协变(covariance)。

逆变定义形式如:trait List[-T] {}

与协变定义相反,类型的泛化方向与参数方向相反,被称为逆变(contravariance),当类型S是类型A的子类型时,则List[A]也可以认为是List[S]的子类

小结:如果A是B的子类,那么在协变中,List[A]就是List[B]的子类,在逆变中,List[A]就是List[B]的父类

package hhb.cn.part11

//大师
class Master

//专家
class Process extends Master

//讲师
class Teacher

//这是一个协变,Process是Master的子类,那么Card[Process] 也是 Card[Master] 的子类
class Card[+T]

class Card2[-T]


object CovarianceDemo {
  //表示只有Card[Master]以及Card[Master]的子类Card[Process]才能进入
  def enterMeet(card: Card[Master]): Unit = {
    println("进入")
  }

  //表示只有Card[Master]以及Card[Master]的子类Card[Process]才能进入
  def enterMeet2(card: Card2[Process]): Unit = {
    println("进入")
  }

  def main(args: Array[String]): Unit = {
    val master = new Card[Master]
    val process = new Card[Process]
    val teacher = new Card[Teacher]
    enterMeet(master)
    enterMeet(process)
    //直接报错
    //enterMeet(teacher)
    val master2 = new Card2[Master]
    val process2 = new Card2[Process]
    val teacher2 = new Card2[Teacher]
    enterMeet2(master2)
    enterMeet2(process2)
    //直接报错
    //enterMeet(teacher2)
  }
}

Akka

Akka是Java虚拟机平台上构建高并发、分布式和容错应用的工具包和运行时。是使用Scala语言编写,同时提供了Scala和Java开发的接口。Akka处理并发的方法基于Actor模型,Actor之间通信的唯一机制就是消息传递。

Actor

Scala的Actor类似与Java 中的多线程,但是不同的是,Scala的Actor提供的模型与多线程不同,Actor尽可能的避免锁和共享状态,从而避免多线程并发时出现资源争用的情况,进而提升多线程编程的性能。

Actor可以看作一个个独立的实体,Actor之间可以通过交换消息的方式进行通信,每个Actor都有自己的收件箱(MailBox)。一个Actor收到其他Actor的信息后,根据需要作出各种响应,消息的类型可以是任意的,消息的内容也可以是任意的。


Akka.png

ActorSystem

在Akka中,ActorSystem是一个重量级结构。他需要分配多个线程,所以在实际应用中,ActorSystem通常是一个单例对象,我们可以使用ActorSystem创建很多Actor。

Akka案例

创建一个maven项目,在项目的pom文件中增加如下依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>ScalaMavenPro</groupId>
  <artifactId>ScalaMavenPro</artifactId>
  <version>1.0-SNAPSHOT</version>
  <!-- 定义一下常量-->
  <properties>
    <encoding>UTF-8</encoding>
    <scala.version>2.13.3</scala.version>
  </properties>

  <dependencies>
    <!-- 添加akka的actor依赖 -->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-actors</artifactId>
      <version>2.11.12</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor -->
    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-actor_2.13</artifactId>
      <version>2.6.8</version>
    </dependency>
    <!-- 多进程之间的Actor通信 -->
    <dependency>
      <groupId>com.typesafe.akka</groupId>
      <artifactId>akka-remote_2.13</artifactId>
      <version>2.6.8</version>
    </dependency>
  </dependencies>
</project>
import akka.actor.{Actor, ActorRef, ActorSystem, Props}

import scala.io.StdIn

class Message extends Actor {
  override def receive: Receive = {
    case "您好" => println("你好呀!")
    case "干什么呢" => println("没干啥")
    case "拜拜" => {
      //关闭自己
      context.stop(self)
      //关闭SystemActor
      context.system.terminate()
    }
  }
}


object ActorDemo {

  def main(args: Array[String]): Unit = {
    val myActorSystem = ActorSystem("myActorSystem")

    val messageActorRef: ActorRef = myActorSystem.actorOf(Props[Message], "message")
    var flag = true
    while (flag) {
      print("请输入想发送的消息:")
      val str = StdIn.readLine()
      messageActorRef ! str
      if (str.equals("拜拜")) {
        flag = false
      }
      Thread.sleep(100)
    }
  }
}
第九个模块错题集.png

相关文章

  • scala-隐式机制及Akka

    隐式机制及Akka 隐式转换 隐式转换和隐式参数时Scala中两个非常强大的功能,利用隐式转换和隐式参数,可以提供...

  • Spring Bean 配置

    Spring Bean 配置三种方式: XML显式配置 Java中显式配置 隐式Bean发现机制和自动装配。 隐式...

  • Spring:装配Bean

    1. 装配Bean 三种装配机制: 在XML中进行显式配置; 在Java中进行显式配置; 隐式的bean发现机制和...

  • Scala-函数式编程

    Scala-函数式编程 1.函数式编程 1.1 面向对象和面向过程 面向对象 按照功能划分问题,将功能分解成不同的...

  • Akka系列(九):Akka分布式之Akka Remote

    Akka作为一个天生用于构建分布式应用的工具,当然提供了用于分布式组件即Akka Remote,那么我们就来看看如...

  • Spring建站补充资料

    一. Spring(bean) 1. Spring bean 的装配机制 XML 显式配置 Java 显式配置 隐...

  • Spring从入门到入坟——Bean的装配机制

    Spring Bean的装配机制Spring中bean有三种装配机制,分别是: 在xml中显式配置;隐式的bean...

  • iOS隐式动画机制

    Core Animation通常对 CALayer 的所有属性(可动画的属性)做动画,在改变layer层一些属性的...

  • Spring提供了三种主要的装配机制

    Spring提供了三种主要的装配机制:隐式的 bean 发现机制和自动装配。在 XML 中进行显式配置。在 Jav...

  • spring学习1.1自动装配Bean-初步接触

    spring 配置bean可选方案 在XML中进行显式配置 在java中进行显式配置 隐式的bean发现机制...

网友评论

      本文标题:scala-隐式机制及Akka

      本文链接:https://www.haomeiwen.com/subject/kgfwqltx.html