Scala入门到精通——第二十三节 高级类型 (二)

https://blog.csdn.net/lovehuangjiaju/article/details/47398179

本节主要内容

  1. 中置类型(Infix Type)
  2. 存在类型
  3. 函数类型
  4. 抽象类型

关于语法糖的问题,在讲解程序语言时,我们常常听到“语法糖”这个术语,在百度百科中,它具有如下定义:

语法糖(Syntactic Sugar),也叫糖衣语法,
是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语。
指的是,在计算机语言中添加某种语法,
种语法能使程序员更方便的使用语言开发程序,
同时增强程序代码的可读性,避免出错的机会;但是这种语法对语言的功能并没有影响。
  • 1
  • 2
  • 3
  • 4
  • 5

例如,泛型它就是一种语法糖,即使不用泛型,也能开发出同等功能的程序,例如排序算法,我可以分别实现Double、Int等类型的排序算法,但是我们使用泛型之后,可以大大简化程序设计,减少重复代码的编写,代码可读性也有所增加。

1. 中置类型(Infix Type)

在scala中存在着中置操作符,如1+2等,+在这被称为中置操作符,前面我们说过,scala中的一切操作皆为对象调用,1+2其实是1.+(2)的对象方法调用。在scala中同样存在着中置类型,如:


class Person[T,S](val name:S,val age:T)

object InfixType extends App {
  //下面的代码是一种中置表达方法,相当于
  //val p:Person[String,Int]=null
  val p:String Person Int=null   
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

可以看到,如果类的泛型参数是两个的话,则可以使用中置表达式进行变量的定义。中置类型最常用的场景是模式匹配,例如:

//定义Person类,两个泛型参数,分别是S,T,因此
//它是可以用中置表达式进行变量定义的
case class Person[S,T](val name:S,val age:T)

object InfixType extends App {
  //下面的代码是一种中置表达方法,相当于
  //val p:Person[String,Int]
  val p:String Person Int= Person("摇摆少年梦",18)

  //中置表达式的模式匹配用法
  //模式匹配时可以直接用常量,也可以直接用变量
  p match {
    case "摇摆少年梦" Person 18=> println("matching is ok")
    case name Person age=> println("name:"+name+"  age="+age)  
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2. 存在类型

在看一些scala语言实现的框架或别人写的程序时,我们常常会发现下列形式定义的变量,例如:

object ExisitType extends App{
  //下面的Array[_]是一种存在类型,虽然用的是类型通配
  //符,但它本质上等同于
  //def print2(x:Array[T] forSome {type T})=println(x)
  //即Array[_]中的类型通匹符也是一种语法糖,用于简化设计
  def print(x:Array[_])=println(x)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

更多的例子如:

object ExisitType extends App{

  def print(x:Array[_])=println(x)

  def print2(x:Array[T] forSome {type T})=println(x)

  //Map[_,_]相当于Map[T,U] forSome {type T;type U}
  def print3(x:Map[_,_])=println(x)

  print(Array("摇摆少年梦","学途无忧网金牌讲师"))
  print2(Array("摇摆少年梦","学途无忧网金牌讲师"))
  print3(Map("摇摆少年梦"->"学途无忧网金牌讲师"))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3. 函数类型

本小节中的部分代码来自:http://hongjiang.info/scala-function-polymorphic/,感谢作者的无私奉献

在scala中函数也是具有类型的,如下面的函数定义方式

//来自API文档中的例子,Function2
object Main extends App {
//max与anonfun2是等价的,它们定义的都是输入参数是两个Int类型
//返回值也是Int类型的函数。
val max = (x: Int, y: Int) => if (x < y) y else x

//通过Funtion2定义一个输入参数为两个整型
//返回类型为Int的函数,这里是通过new创建创建函数
//而这个类正是Function2,它是函数类型类
val anonfun2 = new Function2[Int, Int, Int] {
  def apply(x: Int, y: Int): Int = if (x < y) y else x
}
println(max(0, 1) == anonfun2(0, 1))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Function2对应的类型定义部分代码如下:

trait Function2[@specialized(scala.Int, scala.Long, 
scala.Double) -T1, @specialized(scala.Int, scala.Long, 
scala.Double) -T2, @specialized(scala.Unit, 
scala.Boolean, scala.Int, scala.Float, scala.Long,
 scala.Double) +R] extends AnyRef 
  • 1
  • 2
  • 3
  • 4
  • 5

在scala中还存在单个参数的Function类型即Function1,它的类型定义部分代码如下:

@annotation.implicitNotFound
(msg = "No implicit view available from ${T1} => ${R}.")
trait Function1[@specialized(scala.Int,
 scala.Long, scala.Float, scala.Double/*, 
(scala.Unit, scala.Boolean, scala.Int,
 scala.Float, scala.Long, scala.Double/*,
  scala.AnyRef*/) +R] extends AnyRef
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

下面的代码给出了它的用法:

object Main extends App {
val succ = (x: Int) => x + 1
val anonfun1 = new Function1[Int, Int] {
  def apply(x: Int): Int = x + 1
}
//succ与anonfun1 函数是等价的,它们都定义了输入参数是Int
//返回值类型是Int的函数
assert(succ(0) == anonfun1(0))
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

通过Function1和Function2我们可以看到,其输入参数是逆变的,输出参数是协变的,我们可以通过下面的代码进行验证:

//代码给的是输出类型协变的替代使用
scala> class A; class B; class C extends B
defined class A
defined class B
defined class C
//定义一个输入类型是A,输出类型是C的函数字面量
scala> val x= (p:A)=>new C
x: A => C = <function1>

//下面的代码定义了一个变量x2,它是一个函数类型
//该函数输入是A类型,输出是B类型
//由于B是C的超类,Function1的输出参数又是协变的
//因此下面的代码是合法的
scala>  val x2:A=>B = x
x2: A => B = <function1>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
//代码给的是输入类型是逆变的替代使用
class R; class X; class Y extends X
//创建输入类型是X类型,输出类型是R的函数字面量
val f1 = (x:X)=>new R
//下面的代码定义的变量f2是一个输入类型是Y,返回值类型是R
//的函数字面量,它被赋值为f1,由于输入类型是逆变的,也就是
//说Y是X的子类型,X=>R则是Y=>R的子类型,因此下面的代码是合法的
val f2:Y=>R = f1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

4. 抽象类型

抽象类型是指在类或特质中利用type关键字定义一个没有确定类型的标识,该标识在子类中被确定,称这种类型为抽象类型,例如:

package cn.scala.xtwy.advancedtype

//下面定义了一个抽象类
//抽象类中用type关键字声明了一个抽象类型IndentityType
abstract class Person1{
  type IdentityType
  //方法的返回值类型被声明为抽象类型
  def getIdentityNo():IdentityType
}
//在子类中,对抽象类型进行具体化
class Student extends Person1{
  //将抽象类型具体化为String类型
  type IdentityType=String
  def getIdentityNo()="123"
}
class Teacher extends Person1{
  //将抽象类型具体化为Int类型
  type IdentityType=Int
  def getIdentityNo()=123
}

object AbstractType {
  def main(args: Array[String]): Unit = {
    //返回的是String类型
    println(new Student().getIdentityNo())
  }

}
  • 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

上述代码的也可用泛型进行实现,如:

//使用范型参数将方法的返回值定义为抽象类型
abstract class Person2[T]{
  def getIdentityNo():T
}
//子类带具体的类型String
class Student2 extends Person2[String]{
  def getIdentityNo():String="123"
}
//子类带具体的类型Int
class Teacher extends Person2[Int]{
  def getIdentityNo():Int=123
}
object AbstractType {
  def main(args: Array[String]): Unit = {
    //同样返回String类型
    println(new Student2().getIdentityNo())
  }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在实际应用中,如果类型是在实例化的时候给定的,推荐用类型参数进行类的定义,例如经常需要用到new Person[String,Int](”摇摆少年梦”,18)这种创建对象的方式,此时使用泛型更为方便;如果类型是在子类型中才被确定,则推荐使用抽象类型。例如,从代码的简洁性方面考虑,下面的代码使用抽象类型的话更”省“

//下面是抽象类型的定义方式

trait Closable{
  type in
  type out
  def close(x:in):out
}

class File extends Closable{
  type in=String
  type out=Boolean
  def close(x:in):out= true
  //....其它方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

下面的代码是类型参数的定义方式:

trait Closable[S,T]{
  def close(x:S):T
}

class File extends Closable[String,Boolean]{
  def close(x:String):Boolean= true
  //....其它方法
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

当File类中还有大量的方法要用到String及Boolean类型时,抽象类型的优越性就能表现出来。

猜你喜欢

转载自blog.csdn.net/hellojoy/article/details/81093821