博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
2018年第47周-scala入门-类型参数
阅读量:6940 次
发布时间:2019-06-27

本文共 8333 字,大约阅读时间需要 27 分钟。

类型参数类似于Java中的泛型。注意了,是类型参数,不是参数类型, 顾名思义,就是把类型作为参数。跟Java泛型一样,在集合,类,函数中定义参数类型,然后保证 使用到该类型参数的地方,只能用这种类型,否则编译器报错。在编译期就能发现错误可以大大降低开发成本。

这里说的成本我是很想讨论下,开发成本,不仅仅指的是钱。如你在编写代码时,IDE就提示你错误了。这时候你就不用浪费时间去点击运行才发现错误,从而节省了你的时间,你可能会说时间就是钱啊,实际不那么是,因为得看角度,你的时间或许是你的金钱,但对于老板来说不是,因为你一天无论干什么,发呆、百度资料或做了个功能,老板都是给一样的钱。但开发成本,我觉得能够全面客观的描述这个项目花的劳动时间及资源。

泛型类

泛型类,顾名思义,其实就是在类的声明中,定义一些泛型类型,然后在类的内部,比如field或者method中使用。

使用泛型类,同城需要对类中的某些成员, 比如某些field或method中的参数或变量,进行统一的类型限制,这样可以保证程序更好的健壮性和稳定性。在编译期就能发现错误。
如果不是使用泛型进行统一的类型限制,那么在运行时发现错误,则处理错误的成本就要高很多了。
在使用类的时候,比如创建对象,将类型参数替换为实际的类型即可。或者直接给使用了泛型类的field赋值是,scala会自动进行类型推断。
例子:新生报到,每个学生来自不同的地方,id可能是Int,也可能是String

clas Student[T](val localId:T){  def getSchoolId(id:T) = "S-"+id+"-"+localId}val jc = new Student[Int](700)jc.getSchoolId("jc");jc.getSchoolId(10);

泛型函数

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

与泛型类一样,你可以通过使用了泛型类型的变量传递值来让scala自动推断泛型的是积累下,也可以在调用函数时,手动指定函数类型。
例子:卡片售卖机,可以指定卡片的内容,内容可以是String类型或者Int类型

def getCard[T](content:T)={  if(content.isInstanceOf[Int]) "card:001,"+content   else if(content.isInstanceOf[String]) "card: this your card, "+content   else "card: "+content}

上边界Bounds

在指定泛型类型的是偶,有时我们需要对泛型类型的范围进行界定,而不是可以是任意的类型。比如,我们可能要求某个泛型类型,它就必须是某个类的子类,这样在程序中就可以放心地调用泛型类型继承弗雷的方法,程序才能正常的使用和运行。此时就可以使用上下边界Bounds的特性。

scala的上下边界特性允许泛型类型必须是某个类的子类,或者必须是某个类的弗雷。
上边界Bounds语法: T<:Person
例子:在派对上交朋友

class Person(val name:String){   def sayHello = println("Hello, I'm "+ name)   def makeFriends(p:Person){      sayHello      p.sayHello   }}class Student(name:String) extends Person(name)class Other(name:String)class Party[T<:Person](p1:T,p2:T){  def play = p1.makeFriends(p2);}scala> val s = new Student("jc")s: Student = Student@718632e9scala> val s2 = new Student("jc2")s2: Student = Student@3540ee91scala> val p = new Party(s,s2)p: Party[Student] = Party@77240ed1scala> p.playHello, I'm jcHello, I'm jc2scala> val s3 = new Person("jc2")s3: Person = Person@38c9c7a0scala> val p = new Party(s,s3)p: Party[Person] = Party@fa7e87bscala> val o = new Other("o")o: Other = Other@40cecfc0scala> val p = new Party(s,o)
:14: error: inferred type arguments [Object] do not conform to class Party's type parameter bounds [T <: Person] val p = new Party(s,o) ^
:14: error: type mismatch; found : Student required: T val p = new Party(s,o) ^
:14: error: type mismatch; found : Other required: T val p = new Party(s,o) ^

语法[T<:Person],只要是Person类或及其子类即可。

下边界Bounds

下边界,指定类型必须是某个类的父类

下边界Bounds语法: T>:Child
例子:领身份证

class Father(val name:String)class Child(name:String) extends Father(name)class GrandChild(name:String) extends Child(name)def getIdCard[R>:Child](p:R){  if(p.getClass == classOf[Child]) println("please tell us your parents' names.")     else if(p.getClass == classOf[Father]) println("sign your name to get your child's id card.")    else println("sorry, you are not allowed to get id card.")}val c = new Child("jc child")val p = new Father("jc father")getIdCard(c)getIdCard(p)val g = new GrandChild("jc grandechild")getIdCard(g)  //可执行成功// 想要编译器能发现错误,就只能在调用函数式,指定类型参数scala> getIdCard[GrandChild](g)
:16: error: type arguments [GrandChild] do not conform to method getIdCard's type parameter bounds [R >: Child] getIdCard[GrandChild](g)

View Bounds

上下边界Bounds,虽然可以让一种泛型类型,支持父子关系的多种类型。但是,在某个类与上下边界Bounds指定的父子类型范围内的类都没有任何关系,则默认是肯定不能接受的。

然而,View Bounds作为一种上下边界Bounds的加强版,支持对类型进行隐时转换,将指定的类型进行隐式转换后,再判断是否在边界指定得了类型范围内。
上边界Bounds语法: T>:Person
例子:跟小狗交朋友

class Person(val name:String){  def sayHello = println("Hello, I'm " + name)  def makeFriends(p:Person){    sayHello    p.sayHello   }}class Student(name:String) extends Person(name)//特定于法,<% 代表Person及Person以下的类,可以包括隐式转换的class Party[T<%Person](p1:T,p2:T){  def play = p1.makeFriends(p2);}scala> val s = new Student("jc")s: Student = Student@3e1e52b8scala>  val p = new Person("jcp")p: Person = Person@44f8d156scala> val party = new Party(s,p)party: Party[Person] = Party@587f3671scala> party.playHello, I'm jcHello, I'm jcpclass Dog(val name:String){def sayHello = println("Wang, Wang, I'm "+ name)}scala> val d = new Dog("jdog")d: Dog = Dog@729dc481//报错,因为没有指定隐式转换scala> val party = new Party(s,d)
:14: error: No implicit view available from Object => Person. val party = new Party(s,d) ^implicit def dog2person(dog: Object): Person= if(dog.isInstanceOf[Dog]){val _dog = dog.asInstanceOf[Dog]; new Person(_dog.name)} else Nil //需指定泛型类型,不然party.play就一直卡住scala> val party = new Party[Person](s,d)party: Party[Person] = Party@702a694fscala> party.playHello, I'm jcHello, I'm jdog

Context Bounds

Context Bounds是一种特殊的Bounds,它会根据泛型类型的声明,比如“T:类型”要求必须存在一个类型为“类型[T]”的隐式值。

就是可以使用scala的隐式类,并且可以控制参数类型的一致性
例子: 使用scala内置的比较器比较大小

class Calculator[T: Ordering](val number1:T, val number2:T){  def max(implicit order: Ordering[T]) = if(order.compare(number1,number2)>0) number1 else number2}scala> val c = new Calculator(1,2)c: Calculator[Int] = Calculator@c62b064 scala> c.maxres6: Int = 2scala> val c = new Calculator(1,"2")
:13: error: No implicit Ordering defined for Any. val c = new Calculator(1,"2") ^scala> val c = new Calculator("3","2")c: Calculator[String] = Calculator@409c4578scala> c.maxres7: String = 3

泛型数组

如果想实例化一个泛型数组,需运用到Manifest Context Bounds。也就是说,如果数组元素类型为T的话,需要为类或函数定义[T:Manifest]类型参数。

简单直白地说就是 固定语法,类和函数必须定义[T:Manifest],其里面才能使用泛型数组。

例子: 打包饭菜(不同食物类型分开打包)

class Meat(val name:String)class Vegetable(val name:String)def packageFood[T:Manifest](food: T*) = {  val foodPackage = new Array[T](food.length)  for(i <-0 until food.length) foodPackage(i) = food(i)  foodPackage}scala> packageFood(new Meat("m1"),new Meat("m2"))res8: Array[Meat] = Array(Meat@18f4a2ae, Meat@7e319726)//由于scala有自动推断功能,所以想要编译期发现错误,需指定类型参数scala> packageFood(new Meat("m1"),new Vegetable("v2"))res9: Array[Object] = Array(Meat@4c1bd6ce, Vegetable@16a7770b)//由于scala有自动推断功能,所以想要编译期发现错误,需指定类型参数scala> packageFood[Meat](new Meat("m1"),new Vegetable("v2"))
:16: error: type mismatch; found : Vegetable required: Meat packageFood[Meat](new Meat("m1"),new Vegetable("v2"))

协变和逆变

scala的协变和逆变是非常有特色的,完全解决了java中的泛型的一大遗憾。

举例子说,java中,如果有Professional是Master的子类,那么Card[Professional]是不是Card[Master]的子类?答案是:不是的。
例子:进入会场

class Masterclass Professional extends Master//大师以及大师级别以下的名片都可以进入会场//class Card[+T] (val name:String)def enterMeet(card:Card[Master]){  println("welcome to have this meeting")}scala> val p = new Card[Professional]("p")p: Card[Professional] = Card@49f8e77cscala> enterMeet(p)welcome to have this meetingscala> val m = new Card[Master]("m")m: Card[Master] = Card@125d05e2scala> enterMeet(m)welcome to have this meeting

协变,在上述例子也就是Card[Profession]是Card[Master]的子类,协变的语法是[+T],对比[T]:

scala> class Card[T] (val name:String)defined class Cardscala> val p = new Card[Professional]("p")p: Card[Professional] = Card@6732963bdef enterMeet(card:Card[Master]){  println("welcome to have this meeting")}scala> enterMeet(p)
:15: error: type mismatch; found : Card[Professional] required: Card[Master] enterMeet(p)

逆变,也就是上述例子Card[Master]是Card[Profession]的父类,逆变的语法是[-T],对比[+T]:

scala> class Card[-T](val name:String)defined class Carddef enterMeet(card:Card[Master]){  println("welcome to have this meeting")}scala> val p = new Card[Professional]("p")p: Card[Professional] = Card@584ff37cscala> enterMeet(p)
:15: error: type mismatch; found : Card[Professional] required: Card[Master] enterMeet(p) ^//只要专家级别的名片就可以进入会场,如果大师级别的过来了,当然可以了!def enterMeet(card:Card[Professional]){ println("welcome to have this meeting")}scala> enterMeet(p)welcome to have this meetingscala> val m = new Card[Master]("m")m: Card[Master] = Card@7ef67f5dscala> enterMeet(p)welcome to have this meeting

要点,协变和逆变的语法,可以让原本控制泛型很死的类或函数,扩展到其泛型的父类或子类:

协变,可以让函数enterMeet(card:Card[Master]),原本是只能Master的Card参数,现在只需要Card[+T],即可传入Card[Master]和Card[Professional]参数。也就是可以传入某类及其子类的参数。

协变跟上边界[T<:]不一样,协变讨论的是Card[Professional]是不是Card[Master]的子类。而上边界[T<:]讨论的是Professional是不是Master的子类。从数学上来比喻它们不一样就是,定义域不一样,所以协变跟上边界[T<:Card]不是同个东西,更加没有好对比。

逆变,理解了协变,那逆变就更好理解了,可以让函数enterMeet(card:Card[Professional]),原本是只能Professional的Card参数,现在只需要Card[-T],即可传入Card[Master]和Card[Professional]参数。也就是可以传入某类及其父类的参数。

转载地址:http://uksnl.baihongyu.com/

你可能感兴趣的文章
PHP添加mongodb驱动的问题
查看>>
JS将秒转换为 天-时-分-秒
查看>>
CRUD
查看>>
Unity3D性能优化--- 收集整理的一堆
查看>>
数据库基础
查看>>
数组函数
查看>>
基础概要
查看>>
转-架构高性能网站秘笈(三)——浏览器缓存
查看>>
[SDOI2009][BZOJ 1876]SuperGCD
查看>>
TweenMax动画库学习(六)
查看>>
VueConf 全球首届Vue.js开发者大会资料整理
查看>>
多态 向上,向下转型
查看>>
Linux进程间通信——使用信号量
查看>>
C语言中带有返回类型的函数没有return语句的影响
查看>>
学习笔记之XML
查看>>
高性能js之js文件的加载与解析
查看>>
扭转二次大战战局的关键:雷达英雄传奇
查看>>
类的封装性和信息隐蔽[转 非原创]
查看>>
程序员所具备的素质[转]
查看>>
MySQL-Front 出现“程序注册时间到期 程序将被限制模式下运行”解决方式
查看>>