第二十三节 高级类型 (二)

本节主要内容

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

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

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

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

上百课详细讲解,需要的小伙伴自行百度网盘下载,链接见附件,永久有效。 课程亮点: 1,知识体系完备,从小白到大神各阶段读者均能学有所获。 2,生动形象,化繁为简,讲解通俗易懂。 3,结合工作实践及分析应用,培养解决实际问题的能力。 4,每一块知识点, 都有配套案例, 学习不再迷茫。 课程简介 第一章 环境搭建 00.导学 01.Scala简介 02.Scala程序和Java程序对比 03.Scala环境搭建 04.Scala解释器 05.案例_做最好的自己 第章 变量和数据类型 00.导学 01.输出语句和分号 02.Scala中的常量 03.Scala中的变量 04.字符串的定义 05.惰性赋值 06.标识符 07.数据类型 08.类型转换 09.值类型和String类型之间的相互转换 10.键盘录入功能 11.案例_打招呼 第三章 运算符 001.导学 01.算术运算符 02.赋值运算符 03.关系运算符 04.逻辑运算符 05.进制和8421码 06.原反补码计算规则 07.位运算符 08.案例_交换变量值 第四章 流程控制结构 00.导学 01.流程控制结构之顺序结构 02.选择结构之单分支结构 03.选择结构之双分支结构 04.选择结构之多分支结构 05.选择结构之注意事项 06.选择结构之嵌套分支 07.扩展_块表达式 08.for循环之简单循环 09.for循环之循环嵌套 10.for循环之守卫 11.for循环之推导式 12.while循环 13.do.while循环 14.break和continue的用法 15.综合案例_九九乘法表 16.综合案例_模拟登陆 第五章 方法和函数 00.导学 01.方法入门 02.返回值的类型推断 03.惰性方法 04.方法参数 05.方法调用方式 06.函数入门 07.方法和函数的区别 08.案例_打印nn乘法表 第六章 面向对象入门 00.导学 01.类和对象的相关概念 02.创建类和对象 03.创建类和对象的简写形式 04.定义和访问成员变量 05.使用下划线初始化成员变量 06.定义和访问成员方法 07.访问权限修饰符 08.主构造器 09.辅助构造器 10.定义单例对象 11,在单例对象中定义方法 12.如何定义程序的主入口 13.定义伴生对象 14.private[this]访问权限 15.apply()方法 16.案例_定义工具类 第七章 继承 00.导学 01.继承入门 02.单例对象的继承 03.方法重写 04.isInstanceOf和asInstanceOf 05.getClass和ClassOf关键字 06.抽象类入门 07.抽象字段 08.匿名内部类 09.动物类案例 第八章 特质 00.导学 01.类继承单个特质 02.类继承多个特质 03.单例对象继承特质 04.演示trait中的成员 05.动态混入trait 06.使用trait实现适配器设计模式 07.使用trait实现模板方法模式 08.使用trait实现职责链模式 09.trait的构造机制 10.trait继承class 11.案例_程序员类 第九章 包_样例类_样例对象 00,导学 01.包的简介和格式 02.包的作用域 03.包对象 04.包的可见性 05.包的引入 06.样例类入门案例 07.样例类的默认方法 08.样例对象 09.案例_计算器 第十章 常用容器(数组, 元组, 集合等) 00.导学 01.创建定长数组 02.创建变长数组 03.变长数组的增删改操作 04.遍历数组 05.数组的常用算法 06.创建元组对象 07.访问元组中的元素 08.创建不可变列表 09.创建可变列表 10.可变列表的常用操作 11.列表的常用操作之基础操作 12.列表的常用操作之扁平化 13.列表的常用操作之拉链与拉开 14.列表的常用操作之转换字符串 15.列表的常用操作之求并集,交集,差集 16.创建不可变集 17.不可变集的常见操作 18.创建可变集 19.创建不可变Map 20.创建可变Map 21.Map的基本操作 22.使用迭代器遍历集合 23.函数式编程之foreach 24.函数式编程之简化函数定义 25.函数式编程之映射 26.函数式编程之扁平化映射 27.函数式编程之过滤 28.函数式编程之默认排序 29.函数式编程之指定字段排序 30.函数式编程之自定义排序 31.函数式编程之分组 32.函数式编程之聚合操作 33.函数式编程之折叠操作 34.综合案
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值