scala学习笔记-类型参数(17)

类型参数是什么?

类型参数其实就类似于Java中的泛型。先说说Java中的泛型是什么,比如我们有List a = new ArrayList(),接着a.add(1),没问题,a.add("2"),然后我们a.get(1) == 2,对不对?肯定不对了,a.get(1)获取的其实是个String——"2",String——"2"怎么可能与一个Integer类型的2相等呢? 所以Java中提出了泛型的概念,其实也就是类型参数的概念,此时可以用泛型创建List,List a = new ArrayList[Integer](),那么,此时a.add(1)没问题,而a.add("2")呢?就不行了,因为泛型会限制,只能往集合中添加Integer类型,这样就避免了上述的问题。 那么Scala的类型参数是什么?其实意思与Java的泛型是一样的,也是定义一种类型参数,比如在集合,在类,在函数中,定义类型参数,然后就可以保证使用到该类型参数的地方,就肯定,也只能是这种类型。从而实现程序更好的健壮性。

泛型类

 1 // 泛型类,顾名思义,其实就是在类的声明中,定义一些泛型类型,然后在类内部,比如field或者method,就可以使用这些泛型类型。
 2 // 使用泛型类,通常是需要对类中的某些成员,比如某些field和method中的参数或变量,进行统一的类型限制,这样可以保证程序更好的健壮性和稳定性。
 3 // 如果不使用泛型进行统一的类型限制,那么在后期程序运行过程中,难免会出现问题,比如传入了不希望的类型,导致程序出问题。
 4 // 在使用类的时候,比如创建类的对象,将类型参数替换为实际的类型,即可。
 5 // Scala自动推断泛型类型特性:直接给使用了泛型类型的field赋值时,Scala会自动进行类型推断。
 6
 7 案例:新生报到,每个学生来自不同的地方,id可能是Int,可能是String
 8
 9 class Student[T](val localId: T) {
10   def getSchoolId(hukouId: T) = "S-" + hukouId + "-" + localId
11 }
12
13 val leo = new Student[Int](111)

泛型函数

 1 // 泛型函数,与泛型类类似,可以给某个函数在声明时指定泛型类型,然后在函数体内,多个变量或者返回值之间,就可以使用泛型类型进行声明,从而对某个特殊的变量,或者多个变量,进行强制性的类型限制。
 2 // 与泛型类一样,你可以通过给使用了泛型类型的变量传递值来让Scala自动推断泛型的实际类型,也可以在调用函数时,手动指定泛型类型。
 3
 4 案例:卡片售卖机,可以指定卡片的内容,内容可以是String类型或Int类型
 5 def getCard[T](content: T) = {
 6   if(content.isInstanceOf[Int]) "card: 001, " + content
 7   else if(content.isInstanceOf[String]) "card: this is your card, " + content
 8   else "card: " + content
 9 }
10
11 getCard[String]("hello world")

上边界Bounds

 1 // 在指定泛型类型的时候,有时,我们需要对泛型类型的范围进行界定,而不是可以是任意的类型。比如,我们可能要求某个泛型类型,它就必须是某个类的子类,这样在程序中就可以放心地调用泛型类型继承的父类的方法,程序才能正常的使用和运行。此时就可以使用上下边界Bounds的特性。
 2 // Scala的上下边界特性允许泛型类型必须是某个类的子类,或者必须是某个类的父类
 3
 4 案例:在派对上交朋友
 5 class Person(val name: String) {
 6   def sayHello = println("Hello, I‘m " + name)
 7   def makeFriends(p: Person) {
 8     sayHello
 9     p.sayHello
10   }
11 }
12 class Student(name: String) extends Person(name)
13 class Party[T <: Person](p1: T, p2: T) {
14   def play = p1.makeFriends(p2)
15 }

下边界Bounds

 1 // 除了指定泛型类型的上边界,还可以指定下边界,即指定泛型类型必须是某个类的父类
 2
 3 案例:领身份证
 4 class Father(val name: String)
 5 class Child(name: String) extends Father(name)
 6
 7 def getIDCard[R >: Child](person: R) {
 8   if (person.getClass == classOf[Child]) println("please tell us your parents‘ names.")
 9   else if (person.getClass == classOf[Father]) println("sign your name for your child‘s id card.")
10   else println("sorry, you are not allowed to get id card.")
11 }

View Bounds

 1 // 上下边界Bounds,虽然可以让一种泛型类型,支持有父子关系的多种类型。但是,在某个类与上下边界Bounds指定的父子类型范围内的类都没有任何关系,则默认是肯定不能接受的。
 2 // 然而,View Bounds作为一种上下边界Bounds的加强版,支持可以对类型进行隐式转换,将指定的类型进行隐式转换后,再判断是否在边界指定的类型范围内
 3
 4 案例:跟小狗交朋友
 5 class Person(val name: String) {
 6   def sayHello = println("Hello, I‘m " + name)
 7   def makeFriends(p: Person) {
 8     sayHello
 9     p.sayHello
10   }
11 }
12 class Student(name: String) extends Person(name)
13 class Dog(val name: String) { def sayHello = println("Wang, Wang, I‘m " + name) }
14
15 implicit def dog2person(dog: Object): Person = if(dog.isInstanceOf[Dog]) {val _dog = dog.asInstanceOf[Dog]; new Person(_dog.name) } else Nil
16
17 class Party[T <% Person](p1: T, p2: T)

Context Bounds

1 // Context Bounds是一种特殊的Bounds,它会根据泛型类型的声明,比如“T: 类型”要求必须存在一个类型为“类型[T]”的隐式值。其实个人认为,Context Bounds之所以叫Context,是因为它基于的是一种全局的上下文,需要使用到上下文中的隐式值以及注入。
2
3 案例:使用Scala内置的比较器比较大小
4 class Calculator[T: Ordering] (val number1: T, val number2: T) {
5   def max(implicit order: Ordering[T]) = if(order.compare(number1, number2) > 0) number1 else number2
6 }

Manifest Context Bounds

 1 // 在Scala中,如果要实例化一个泛型数组,就必须使用Manifest Context Bounds。也就是说,如果数组元素类型为T的话,需要为类或者函数定义[T: Manifest]泛型类型,这样才能实例化Array[T]这种泛型数组。
 2
 3 案例:打包饭菜(一种食品打成一包)
 4 class Meat(val name: String)
 5 class Vegetable(val name: String)
 6
 7 def packageFood[T: Manifest] (food: T*) = {
 8   val foodPackage = new Array[T](food.length)
 9   for(i <- 0 until food.length) foodPackage(i) = food(i)
10   foodPackage
11 }

协变和逆变

 1 // Scala的协变和逆变是非常有特色的!完全解决了Java中的泛型的一大缺憾!
 2 // 举例来说,Java中,如果有Professional是Master的子类,那么Card[Professionnal]是不是Card[Master]的子类?答案是:不是。因此对于开发程序造成了很多的麻烦。
 3 // 而Scala中,只要灵活使用协变和逆变,就可以解决Java泛型的问题。
 4
 5 案例:进入会场
 6 class Master
 7 class Professional extends Master
 8
 9 // 大师以及大师级别以下的名片都可以进入会场
10 class Card[+T] (val name: String)
11 def enterMeet(card: Card[Master]) {
12   println("welcome to have this meeting!")
13 }
14
15 // 只要专家级别的名片就可以进入会场,如果大师级别的过来了,当然可以了!
16 class Card[-T] (val name: String)
17 def enterMeet(card: Card[Professional]) {
18   println("welcome to have this meeting!")
19 }

Existential Type

1 // 在Scala里,有一种特殊的类型参数,就是Existential Type,存在性类型。这种类型务必掌握是什么意思,因为在spark源码实在是太常见了!
2
3 Array[T] forSome { type T }
4 Array[_]
时间: 2024-12-15 01:55:06

scala学习笔记-类型参数(17)的相关文章

scala学习笔记-类型参数中协变(+)、逆变(-)、类型上界(&lt;:)和类型下界(&gt;:)的使用

转载自  fineqtbull   http://fineqtbull.iteye.com/blog/477994 有位je上的同学来短信向我问起了Scala类型参数中协变.逆变.类型上界和类型下界的使用方法和原理,自己虽然也刚学不久,在主要调查了<Programing in Scala>的19章后,试着在下面做一个总结.如有错误之处还请各位指正. 先说说协变和逆变(实际上还有非变).协变和逆变主要是用来解决参数化类型的泛化问题.由于参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数

Scala学习笔记及与Java不同之处总结-从Java开发者角度

Scala与Java具有很多相似之处,但又有很多不同.这里主要从一个Java开发者的角度,总结在使用Scala的过程中所面临的一些思维转变. 这里仅仅是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.以下列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码演示样例及具体阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接. 语句能够不用";"结束.且推荐不适用";". 变量声明时以var或va

原创:Scala学习笔记(不断更新)

Scala是一种函数式语言和面向对象语言结合的新语言,本笔记中就零散记下学习scala的一些心得,主要侧重函数式编程方面. 1. 以递归为核心控制结构. 实现循环处理的方式有三种:goto,for/while,递归,其中用goto实现循环已经在现代语言中被放弃,而for/while形式的结构化编程成为主流,而递归作为另一种方案,则长期只流行在函数式编程的小圈子中. 递归被主流编程界所担心的主要是过深的调用栈,甚至以前的课堂上我们还亲自尝试过将递归改写为循环,但是现代函数式编程语言中,通过尾递归(

Scala学习笔记及与Java不同之处总结-从Java开发人员角度

Scala与Java具有许多相似之处,但又有许多不同.这里主要从一个Java开发人员的角度,总结在使用Scala的过程中所面临的一些思维转变.这里只是总结了部分两种语言在开发过程中的不同,以后会陆续更新一些切换后在开发过程中值得注意的地方.下面列举了部分,但令人印象深刻的Scala语言的不同之处,具体的代码示例及详细阐述见下文. ? Scala中可直接调用Java代码,与Java无缝连接: 语句可以不用";"结束,且推荐不适用";": 变量声明时以var或val开头

Scala学习笔记一之基础语法,条件控制,循环控制,函数,数组,集合

前言:Scala的安装教程:http://www.cnblogs.com/biehongli/p/8065679.html 1:Scala之基础语法学习笔记: 1:声明val变量:可以使用val来声明变量,用来存放表达式的计算结果,但是常量声明后是无法改变它的值的,建议使用val来声明常量: 声明var变量:如果要声明可以改变的引用,可以使用var变量,声明的常量的值可以改变. 3:指定类型:无论声明val变量还是声明var变量.都可以手动指定其类型,如果不指定,scala会自动根据值,进行类型

Scala学习笔记-环境搭建以及简单语法

关于环境的搭建,去官网下载JDK8和Scala的IDE就可以了,Scala的IDE是基于Eclipse的. 下面直接上代码: 这是项目目录: A是scala写的: package first import scala.collection.mutable.ListBuffer object A { def main(args: Array[String]) { print("Hello,Scala");//学习程序设计的第一句 println("---");//pr

Scala学习笔记

1.在Scala中,变量或函数的类型总是写在变量或函数的名称的后面. 2.Scala并不区分基本类型和引用类型 3.定义常量用val,定义变量用var 通过val声明的只会生成getter,而var会生成setter,getter 4.java中的++ i和i ++在Scala里不起作用 5.参数化:在创建实例的同时就完成对它的“设置” 6.a*b:左操作数是方法的调用者,可以写成a.*(b) 但1:: twoThree,:: 方法的调用者是twoThree,1是方法的传入参数,可以写成twoT

scala 学习笔记(03) 参数缺省值、不定个数参数、类的属性(Property)

继续学习,这一篇主要是通过scala来吐槽java的,同样是jvm上的语言,差距咋就这么大呢? 作为一个有.NET开发经验的程序员,当初刚接触java时,相信很多人对java语言有以下不爽(只列了极小一部分): 1. 一堆的setter/getter方法,没有c#中的property属性概念 2. 方法的参数值,不能设置缺省值 3. 不定个数参数的写法太单一 ... 然后java的拥护者讲出一堆大道理,说这样设计是如何如何有道理,各种洗脑,时间长了,也就被迫习惯了.要不是遇到scala,我还真就

scala 学习笔记(04) OOP(上)

一.主从构造器 java中构造函数没有主.从之分,只有构造器重载,但在scala中,每个类都有一个主构造器,在定义class时,如果啥也没写,默认有一个xxx()的主构造器 class Person { var name: String = _ /** * 从构造器 * @param name */ def this(name: String) = { this //注意:从构造器,必须先调用主构造器 this.name = name; } override def toString = { "