类型参数

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

泛型类

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

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

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

在使用泛型类的时候,比如创建泛型类的对象,只需将类型参数替换为实际的类型即可。

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

泛型类的定义如下:

1
2
3
4
5
6
7
8
9
//定义一个泛型类
class Stack[T1, T2, T3](name: T1) {
var age: T2 = _
var address: T3 = _

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

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

1
2
3
4
5
6
7
8
9
10
object GenericityDemo {
def main(args: Array[String]): Unit = {
//创建泛型类的对象
val stack=new Stack[String,Int,String]("lisi")
stack.age=20
stack.address="北京"

stack.getInfo
}
}

泛型函数

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
object GenericityFunction {
def getCard[T](content: T) = {
content match {
case content: Int => s"card:$content is Int "
case content: String => s"card:$content is String"
case _ => s"card:$content"
}
}

def main(args: Array[String]): Unit = {
println(getCard[String]("hello"))
println(getCard(1001))
}
}

协变和逆变

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

举例来说,Java中,如果有Professional是Master的子类,那么Card[Professionnal]是不是Card[Master]的子类?答案是:不是。因此对于开发程序造成了很多的麻烦。

而Scala中,只要灵活使用协变和逆变,就可以解决Java泛型的问题。

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

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

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

当类型S是类型A的子类型,则Queue[A]反过来可以认为是Queue[S}的子类型,也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变(contravariance)。

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

协变案例:只有大师以及大师级别以下的名片都可以进入会场

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package lagou.cn.part11

//大师
class Master

//专家
class Professor extends Master

//讲师
class Teacher

//定义协变
//class Card[+T]

//定义逆变
class Card[-T]

object ConvarianceDemo {
def enterMeet(card: Card[Professor]): Unit = {
//只有Card[Master]和它的子类才能进入会场
println("欢迎进入会场!")
}

def main(args: Array[String]): Unit = {
val masterCard = new Card[Master]
val professorCard = new Card[Professor]
val teacherCard = new Card[Teacher]

enterMeet(masterCard)
enterMeet(professorCard)
// enterMeet(teacherCard)
}
}

Akka

Akka是Java虚拟机平台上构建高并发、分布式和容错应用的工具包和运行时。

Akka用Scala语言编写,同时提供了Scala和Java的开发接口。

Akka处理并发的方法基于Actor模型,Actor之间通信的唯一机制就是消息传递。

  • Actor

    Scala的Actor类似于Java中的多线程编程。

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

    Actor可以看作是一个个独立的实体,Actor之间可以通过交换消息的方式进行通信,每个Actor都有自己的收件箱(Mailbox)。

    一个Actor收到其他Actor的信息后,根据需要作出各种相应。消息的类型可以是任意的,消息的内容也可以是任意的。


  • ActorSystem

    在Akka中,ActorSystem是一个重量级的结构。

    它需要分配多个线程,所以在实际应用中,ActorSystem通常是一个单例对象,我们可以使用这个ActorSystem创建很多Actor。

  • Akka案例

    创建一个maven项目,支持Scala开发






    在项目的pom文件中增加如下依赖:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    <properties>
    <encoding>UTF-8</encoding>
    <scala.version>2.12.3</scala.version>
    <scala.compat.version>2.11</scala.compat.version>
    <akka.version>2.4.17</akka.version>
    </properties>

    <dependencies>
    <!-- 添加akka的actor依赖 -->
    <dependency>
    <groupId>org.scala-lang</groupId>
    <artifactId>scala-actors</artifactId>
    <version>2.11.8</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor -->
    <dependency>
    <groupId>com.typesafe.akka</groupId>
    <artifactId>akka-actor_2.11</artifactId>
    <version>2.3.16</version>
    </dependency>

    <!-- 添加akka的actor依赖 -->
    <dependency>
    <groupId>com.typesafe.akka</groupId>
    <artifactId>akka-actor_${scala.compat.version}</artifactId>
    <version>${akka.version}</version>
    </dependency>

    <!-- 多进程之间的Actor通信 -->
    <!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-remote -->
    <dependency>
    <groupId>com.typesafe.akka</groupId>
    <artifactId>akka-remote_${scala.compat.version}</artifactId>
    <version>${akka.version}</version>
    </dependency>
    </dependencies>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    import akka.actor.{Actor, ActorRef, ActorSystem, Props}

    import scala.io.StdIn

    class HelloActor extends Actor {
    //接收消息并进行处理
    override def receive: Receive = {
    case "吃了吗" => println("吃过了")
    case "吃的啥" => println("北京烤鸭")
    case "拜拜" => {
    //关闭自己
    context.stop(self)
    //关闭ActorSystem
    context.system.terminate()
    }
    }
    }

    object HelloActor {

    //通过ActorSystem创建线程池对象myFactory
    private val myFactory = ActorSystem("myFactory")
    //通过myFactory.actorOf来创建一个Actor
    private val helloActorRef: ActorRef = myFactory.actorOf(Props[HelloActor], "helloActor")

    def main(args: Array[String]): Unit = {
    var flag = true
    while (flag) {
    print("请输入发送的消息:")
    val consoleLine: String = StdIn.readLine()
    //!发送消息
    helloActorRef ! consoleLine
    if (consoleLine.equals("拜拜")) {
    flag = false
    println("程序即将结束!")
    }

    //让程序休眠100毫秒
    Thread.sleep(100)
    }
    }
    }