五.cala语言的高级特性

1.什么是泛型类

和Java或者C++一样,类和特质可以带类型参数。在Scala中,使用方括号来定义类型参数

/**

* 需求:定义一个类,来操作整数

*/

class GenericClassInt {

private var content:Int = 10

def set(value:Int) = {content = value}

def get() :Int = {content}

}

/**

* 需求:定义一个类,来操作字符串

*/

class GenericClassString {

private var content:String = ""

def set(value:String) = {content = value}

def get() :String = {content}

}

/**

* 问题:能不能定义一个类,既可以操作整数,也可以操作字符串

*

* 解决:定义泛型类

*/

class GenericClass[T] {

private var content:T = _ // 初始值用 _ 表示

def set(value:T) = {content = value}

def get() :T = {content}

}

object GenericClass {

def main(args: Array[String]): Unit = {

var v1 = new GenericClass[Int] // 相当于 GenericClassInt

v1.set(10000)

println(v1.get())

var v2 = new GenericClass[String]

v2.set("Hello")

println(v2.get())

}

}

2.什么是泛型函数

函数和方法也可以带类型参数。和泛型类一样,我们需要把类型参数放在方法名之后。

注意:这里的ClassTag是必须的,表示运行时的一些信息,比如类型。

scala> def mkIntArray(elem:Int*) = Array[Int](elem:_*)

mkIntArray: (elem: Int*)Array[Int]

scala> mkIntArray(1,2,3)

res25: Array[Int] = Array(1, 2, 3)

scala> mkIntArray(1,2,3,4,5,6)

res26: Array[Int] = Array(1, 2, 3, 4, 5, 6)

scala> def mkArray[T:ClassTag](elem:T*) = Array[T](elem:_*)

<console>:11: error: not found: type ClassTag

def mkArray[T:ClassTag](elem:T*) = Array[T](elem:_*)

^

<console>:11: error: No ClassTag available for T

def mkArray[T:ClassTag](elem:T*) = Array[T](elem:_*)

^

scala> import scala.reflect.ClassTag

import scala.reflect.ClassTag

scala> def mkArray[T:ClassTag](elem:T*) = Array[T](elem:_*)

mkArray: [T](elem: T*)(implicit evidence$1:

scala.reflect.ClassTag[T])Array[T]

scala> mkArray(1,2,3)

res27: Array[Int] = Array(1, 2, 3)

scala> mkArray("123","456")

res28: Array[String] = Array(123, 456)

3.Upper Bounds 与 Lower Bounds

类型的上界和下界,是用来定义类型变量的范围。它们的含义如下:

S <: T

这是类型上界的定义。也就是S必须是类型T的子类(或本身,自己也可以认为是自己的子类。

U >: T

这是类型下界的定义。也就是U必须是类型T的父类(或本身,自己也可以认为是自己的父类)。

scala> def addTwoString[T <: String](x:T,y:T) = x + " *** " + y

addTwoString: [T <: String](x: T, y: T)String

scala> addTwoString("123","456")

res29: String = 123 *** 456

scala> addTwoString(1,2)

<console>:14: error: inferred type arguments [Int] do not conform to method

addTwoString's type parameter bounds [T <: String]

addTwoString(1,2)

^

<console>:14: error: type mismatch;

found : Int(1)

required: T

addTwoString(1,2)

^

<console>:14: error: type mismatch;

found : Int(2)

required: T

addTwoString(1,2)

^

scala> addTwoString(1.toString,2.toString)

res31: String = 1 *** 2

4.视图界定(View bounds)

它比 <: 适用的范围更广,除了所有的子类型,还允许隐式转换过去的类型。用 <% 表示。尽量使用视图界定,来取代泛型的上界,因为适用的范围更加广泛。

scala> def addTwoString[T <% String](x:T,y:T) = x + " *** " + y

addTwoString: [T](x: T, y: T)(implicit evidence$1: T => String)String

scala> addTwoString(1,2)

<console>:14: error: No implicit view available from Int => String.

addTwoString(1,2)

^

scala> implicit def int2String123123(n:Int):String = n.toString

warning: there was one feature warning; re-run with -feature for details

int2String123123: (n: Int)String

scala> addTwoString(1,2)

res34: String = 1 *** 2

//主意:不要多次定义相同功能的隐式函数

scala> implicit def int2String12sdfdf3123(n:Int):String = n.toString

warning: there was one feature warning; re-run with -feature for details

int2String12sdfdf3123: (n: Int)String

scala> addTwoString(1,2)

<console>:16: error: ambiguous implicit values:

both method int2String123123 of type (n: Int)String

and method int2String12sdfdf3123 of type (n: Int)String

match expected type Int => String

addTwoString(1,2)

5.协变和逆变

协变:

Scala的类或特征的范型定义中,如果在类型参数前面加入+符号,就可以使类或特征变为协变了。

/**

* 协变

* */

//动物类,父类

class Animal {}

//鸟类,动物类的子类

class Brid extends Animal{}

//吃的类

class Eat[+T](t:T){

}

/**

* 测试

* */

object Test{

def main(args: Array[String]): Unit = {

//创建鸟吃东西的对象

var brid:Eat[Brid] = new Eat[Brid](new Brid)

//创建一个动物吃东西的对象

//问题是,能否将鸟吃东西的对象赋值个动物吃东西的对象

//原因是:Brid是Animal的子类,但是Eat[Brid]不是Eat[Animal]的子类

var animal:Eat[Animal] = brid

}

}

逆变:

在类或特征的定义中,在类型参数之前加上一个-符号,就可定义逆变范型类和特征了。

/**

* 逆变

* */

//动物类,父类

class Animal {}

//鸟类,动物类的子类

class Brid extends Animal{}

//鹦鹉,鸟的子类

class Parrot extends Brid{}

//吃的类

class Eat[-T](t:T){

}

/**

* 测试

* */

object Test{

def main(args: Array[String]): Unit = {

//创建鸟吃东西的对象

var brid:Eat[Brid] = new Eat[Brid](new Brid)

//创建一个鹦鹉吃东西的对象

//问题是,能否将鸟吃东西的对象赋值个鹦鹉吃东西的对象

//原因是:Brid是Parrot的父类,但是Eat[Brid]不是Eat[Parrot]的父类

var animal:Eat[Parrot] = brid

}

}

总结:Scala的协变:泛型变量的值可以是本身类型或者其子类的类型

Scala的逆变:泛型变量的值可以是本身类型或者其父类的类型

6.隐式转换函数

所谓隐式转换函数指的是以implicit关键字申明的带有单个参数的函数。

/**

* 隐式函数

* */

//猴子类

class Monkey(f:Fruit) {

def eat(): Unit ={

println("猴子吃"+f.getFruitName)

}

}

//水果类

class Fruit(name:String){

def getFruitName: String ={

name

}

}

/**

* 测试

* 需求:猴子吃香蕉

* */

object Test{

def main(args: Array[String]): Unit = {

//正常调用

var monkey = new Monkey(new Fruit("香蕉"))

monkey.eat()

//问题:能不能定义香蕉类的时候直接调用吃的方法

//定义隐式函数

val f:Fruit = new Fruit("香蕉")

f.eat()

}

implicit def fruitToMonkey(f:Fruit): Monkey ={

new Monkey(f)

}

}

7.隐式参数

使用implicit申明的函数参数叫做隐式参数。我们也可以使用隐式参数实现隐式的转换

/**

* 隐式参数

* */

object Demo1 {

/**

* 定义一个有参数的方法

* 问题是:如何在调用方法的时候不传递参数

* */

implicit var n = "张三"

def ss(implicit name:String): Unit ={

println("我的名字是:"+name)

}

def main(args: Array[String]): Unit = {

ss

}

}

/**

*定义一个方法可以进行任意类型数据的比较

*/

def smaller[T](x:T,y:T)(implicit order:T => Ordered[T]): T ={

if(x > y) x else y

}

8.隐式类

所谓隐式类: 就是对类增加implicit 限定的类,其作用主要是对类的功能加强!

/**

* 隐式类

* 需求,通过n.add(m)实现n+m的值,也就是实现两个数的和

* */

object Demo2 {

//定义隐式类

implicit class ADD1(x:Int){

def add(a:Int) = a+x

}

def main(args: Array[String]): Unit = {

println(5.add(10))

}

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 数组是一种可变的、可索引的数据集合。在Scala中用Array[T]的形式来表示Java中的数组形式 T[]。 v...
    时待吾阅读 948评论 0 0
  • Databricks Scala 编程风格指南 本文转载自 https://github.com/databric...
    雪轩辕阅读 1,168评论 0 1
  • 欢迎转载 请注明出处 scala隐式类型对精简代码和提升可读性上有很大的帮助,一直是解零零碎碎学习,从没有系统梳理...
    小赵营阅读 520评论 0 2
  • 变量初始化可以用用 _ 作占位符,赋值为默认值,字符串 null,Float、Int、Double 等为 0var...
    FaDeo_O阅读 903评论 0 0
  • Scala高级特性 Scala混合了面向对象和函数式的特性,我们通常将可以作为参数传递到方法中的表达式叫做函数。在...
    顿悟之树阅读 374评论 0 0