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:_*)
def mkArray[T:ClassTag](elem:T*) = Array[T](elem:_*)
^
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)
addTwoString's type parameter bounds [T <: String]
addTwoString(1,2)
^
found : Int(1)
required: T
addTwoString(1,2)
^
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)
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)
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))
}
}