十八、泛型、上下界、视图界定 、上下文界定
18.1 泛型的基本介绍
18.1.1 基本介绍
1)
如果我们要求函数的参数可以接受任意类型。可以使用泛型,这个类型可以代表任意的数据类
型。
2)
例如
List
,在创建
List
时,可以传入整型、字符串、浮点数等等任意类型。那是因为
List
在
类定义时引用了泛型。比如在
Java
中:
public interface List<E> extends Collection<E>
18.1.2 Scala 泛型应用案例 1
要求
:
1)
编写一个
Message
类
2)
可以构建
Int
类型的
Message,String
类型的
Message.
3)
要求使用泛型来完成设计
,(
说明:不能使用
Any)
object GenericDemo01 {
def main(args: Array[String]): Unit = {
val intMessage = new IntMessage[Int](10)
println(intMessage)
val strMessage = new StringMessage[String]("hello")
println(strMessage)
}
}
/*
编写一个Message类
可以构建Int类型的Message,
String类型的Message.
要求使用泛型来完成设计,(说明:不能使用Any)
*/
abstract class Message[T](s:T) {
def get = s
}
class IntMessage[Int](v:Int) extends Message(v)
class StringMessage[String](v:String) extends Message(v)
18.1.3 Scala 泛型应用案例 2
要求
1)
请设计一个
EnglishClass (
英语班级类
)
,在创建
EnglishClass
的一个实例时,需要指定
[
班级开
班季节
(spring,autumn,summer,winter)
、班级名称、班级类型
]
2)
开班季节只能是指定的,班级名称为
String,
班级类型是
(
字符串类型
"
高级班
", "
初级班
"..)
或
者是
Int
类型
(1, 2, 3
等
)
3)
请使用泛型来完成本案例
.
object GenericDemo02 {
def main(args: Array[String]): Unit = {
//使用
val class01 = new EnglishClass[SeasonEnum.SeasonEnum,String,String](SeasonEnum.spring,"0705班","高级班")
println("class01 " + class01.classSesaon + " " + class01.className + class01.classType)
val class02 = new EnglishClass[SeasonEnum.SeasonEnum,String,Int](SeasonEnum.spring,"0707班",1)
println("class02 " + class02.classSesaon + " " + class02.className + class02.classType)
}
}
/*
Scala泛型应用案例2
要求
请设计一个EnglishClass (英语班级类),在创建EnglishClass的一个实例时,需要指定[ 班级开班季节(spring,autumn,summer,winter)、班级名称、班级类型]
开班季节只能是指定的,班级名称为String, 班级类型是(字符串类型 "高级班", "初级班"..) 或者是 Int 类型(1, 2, 3 等)
请使用泛型来完成本案例.
*/
class EnglishClass[A, B, C](val classSesaon: A, val className: B, val classType: C)
//季节是枚举类型
class SeasonEnum extends Enumeration {
type SeasonEnum = Value
val spring,autumn,summer,winter = Value
}
18.1.4 Scala 泛型应用案例 3
要求
1)
定义一个函数,可以获取各种类型的
List
的中间
index
的值
2)
使用泛型完成
object GenericDemo03 {
def main(args: Array[String]): Unit = {
val list1 = List("hello", "dog", "world")
val list2 = List(90, 10, 23)
println(midList[String](list1))// "dog"
println(midList[Int](list2))// 10
}
/*
要求
定义一个函数,可以获取各种类型的 List 的中间index的值
使用泛型完成
*/
def midList[E](l: List[E]): E = {
l(l.length / 2)
}
}
18.2 类型约束-上界(Upper Bounds)/下界(lower bounds)
18.2.1 上界(Upper Bounds)介绍和使用
java
中上界
在
Java
泛型里表示某个类型是
A
类型的子类型,使用
extends
关键字,这种形式叫
upper
bounds(
上限或上界
)
,语法如下:
<T extends A>
//
或用通配符的形式:
<? extends A>
18.2.2 上界(Upper Bounds)介绍和使用
scala
中上界
在
scala
里表示某个类型是
A
类型的子类型,也称上界或上限,使用
<:
关键字,语法如下:
[T <: A]
//
或用通配符
:
[_ <: A]
18.2.3 上界(Upper Bounds)介绍和使用
scala
中上界应用案例
-
要求
1)
编写一个通用的类,可以进行
Int
之间、
Float
之间、等实现了
Comparable
接口的值直接的比
较
.//java.lang.Integer
2)
分别使用
传统方法
和
上界的方式
来完成,体会上界使用的好处
.
3)
代码
object UpperBoundsDemo01 {
def main(args: Array[String]): Unit = {
val compareInt = new CompareInt(10,40)
println(compareInt.greater) // 40
//第一个用法
val commonCompare1 = new CommonCompare(Integer.valueOf(10), Integer.valueOf(40))//Int
println(commonCompare1.greater)
//第二个用法
val commonCompare2 = new CommonCompare(java.lang.Float.valueOf(1.1f), java.lang.Float.valueOf(2.1f))//Fl
println(commonCompare2.greater)
//第3种写法使用了隐式转换
//implicit def float2Float(x: Float): java.lang.Float = x.asInstanceOf[java.lang.Float]
val commonCompare3 = new CommonCompare[java.lang.Float](10.1f, 21.1f)//
println(commonCompare3.greater)
}
}
/*
编写一个通用的类,可以进行Int之间、Float之间、等实现了Comparable接口的值直接的比较.//java.lang.Integer
分别使用传统方法和上界的方式来完成,体会上界使用的好处.
*/
//传统方法
class CompareInt(n1: Int, n2: Int) {
//返回较大的值
def greater = if(n1 > n2) n1 else n2
}
//使用上界(上限)来完成
//说明
//1. [T <: Comparable[T]] 表示T类型是Comparable 子类型
//2. 即你传入的T类要继承Comparable接口
//3. 这样就可以使用compareTo方法
//4. 这样的写法(使用上界的写法)通用性比传统的好
class CommonCompare[T <: Comparable[T]](obj1:T,obj2:T) {
def greater = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}
18.2.4 下界(Lower Bounds)介绍和使用
Java
中下界
在
Java
泛型里表示某个类型是
A
类型的父类型,使用
super
关键字
<T super A>
//
或用通配符的形式:
<? super A>
scala
中下界
在
scala
的下界或下限,使用
>:
关键字,语法如下:
[T >: A]
//
或用通配符
:
[_ >: A]
scala
中下界应用实例
//1)和Animal直系的,是Animal父类的还是父类处理,是Animal子类的按照Animal处理(),
//2)和Animal无关的,一律按照Object处理!
object LowerBoundsDemo01 {
def main(args: Array[String]): Unit = {
println("ok!")
//满足下界的约束
biophony(Seq(new Earth, new Earth)).map(_.sound())
//满足下界的约束
biophony(Seq(new Animal, new Animal)).map(_.sound())
//这里我们不能使用上界的思路去推导,这里是可以运行
//1.?
println("===================")
biophony(Seq(new Bird, new Bird)).map(_.sound())//
//biophony(Seq(new Moon))
}
//下界
def biophony[T >: Animal](things: Seq[T]) = things
}
class Earth { //Earth 类
def sound(){ //方法
println("hello !")
}
}
class Animal extends Earth{
override def sound() ={ //重写了Earth的方法sound()
println("animal sound")
}
}
class Bird extends Animal{
override def sound()={ //将Animal的方法重写
print("bird sounds")
}
}
class Moon {
// def sound()={ //将Animal的方法重写
// print("bird sounds")
// }
}
scala
中下界的使用小结
def biophony[T >: Animal](things: Seq[T]) = things
1)
对于下界,可以传入任意类型
2)
传入和
Animal
直系的,是
Animal
父类的还是父类处理,是
Animal
子类的按照
Animal
处理
3)
和
Animal
无关的,一律按照
Object
处理
4)
也就是下界,可以随便传,只是处理是方式不一样
5)
不能使用上界的思路来类推下界的含义
18.2.5 视图界定应用案例 3
说明
:
自己写
隐式转换
结合
视图界定
的方式,比较两个
Person
对象的年龄大小
object ViewBoundsDemo03 {
def main(args: Array[String]): Unit = {
val p1 = new Person3("汤姆", 13)
val p2 = new Person3("杰克", 10)
//引入隐式函数
import MyImplicit._
val compareComm3 = new CompareComm3(p1,p2)
println(compareComm3.getter)
}
}
class Person3(val name: String, val age: Int) {
//这里是重写toString,为了显示方便
override def toString: String = this.name + "\t" + this.age
}
//说明
//1. T <% Ordered[T] 表示T是Ordered子类型 java.lang.Comparable
//2. 这里调用的compareTo方法是 T这个类型的方法
class CompareComm3[T <% Ordered[T]](obj1: T, obj2: T) {
def getter = if (obj1 > obj2) obj1 else obj2
def geatter2 = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}
object MyImplicit {
implicit def person3toOrderedPerson3(p3:Person3) = new Ordered[Person3] {
override def compare(that: Person3) = { //是你自己的业务逻辑
p3.age - that.age
}
}
}
18.3 类型约束-上下文界定(Context bounds)
18.3.1 基本介绍
与
view bounds
一样
context bounds(
上下文界定
)
也是
隐式参数的语法糖
。为语法上的方便, 引入
了”上下文界定”这个概念
18.3.2 上下文界定应用实例
要求:使用上下文界定
+
隐式参数的方式,比较两个
Person
对象的年龄大小
要求:使用
Ordering
实现比较
代码
object ContextBoundsDemo {
//这里我定义一个隐式值 Ordering[Person]类型
implicit val personComparetor = new Ordering[Person4] {
override def compare(p1: Person4, p2: Person4): Int =
p1.age - p2.age
}
def main(args: Array[String]): Unit = {
//
val p1 = new Person4("mary", 30)
val p2 = new Person4("smith", 35)
val compareComm4 = new CompareComm4(p1, p2)
println(compareComm4.geatter) // "smith", 35
val compareComm5 = new CompareComm5(p1, p2)
println(compareComm5.geatter) // "smith", 35
println("personComparetor hashcode=" + personComparetor.hashCode())
val compareComm6 = new CompareComm6(p1, p2)
println(compareComm6.geatter) // "smith", 35
}
}
//一个普通的Person类
class Person4(val name: String, val age: Int) {
//重写toStirng
override def toString = this.name + "\t" + this.age
}
//方式1
//说明:
//1. [T: Ordering] 泛型
//2. obj1: T, obj2: T 接受T类型的对象
//3. implicit comparetor: Ordering[T] 是一个隐式参数
class CompareComm4[T: Ordering](obj1: T, obj2: T)(implicit comparetor: Ordering[T]) {
def geatter = if (comparetor.compare(obj1, obj2) > 0) obj1 else obj2
}
//方式2
//方式2,将隐式参数放到方法内
class CompareComm5[T: Ordering](o1: T, o2: T) {
def geatter = {
def f1(implicit cmptor: Ordering[T]) = cmptor.compare(o1, o2) //返回一个数字
//如果f1返回的值>0,就返回o1,否则返回o2
if (f1 > 0) o1 else o2
}
def lowwer = {
def f1(implicit cmptor: Ordering[T]) = cmptor.compare(o1, o2) //返回一个数字
//如果f1返回的值>0,就返回o1,否则返回o2
if (f1 > 0) o2 else o1
}
}
//方式3
//方式3,使用implicitly语法糖,最简单(推荐使用)
class CompareComm6[T: Ordering](o1: T, o2: T) {
def geatter = {
//这句话就是会发生隐式转换,获取到隐式值 personComparetor
//底层仍然使用编译器来完成绑定(赋值的)工作
val comparetor = implicitly[Ordering[T]]
println("comparetor hashcode=" + comparetor.hashCode())
if (comparetor.compare(o1, o2) > 0) o1 else o2
}
}
18.4 协变、逆变和不变
18.4.1 基本介绍
1) Scala
的协变
(+)
,逆变
(-)
,协变
covariant
、逆变
contravariant
、不可变
invariant
2)
对于一个带类型参数的类型,比如
List[T]
,如果对
A
及其子类型
B
,满足
List[B]
也符合
List[A]
的子类型,那么就称为
covariance(
协变
)
,如果
List[A]
是
List[B]
的子类型,即与原来的父子关系正相反,则称为 contravariance(
逆变
)
。如果一个类型支持协变或逆变,则称这个类型为
variance(
翻译为可变的或变型)
,否则称为
invariance(
不可变的
)
3)
在
Java
里,泛型类型都是
invariant
,比如
List<String>
并不是
List<Object>
的子类型。而
scala 支持,可以在定义类型时声明(
用加号表示为协变,减号表示逆变
)
,如
: trait List[+T] //
在类型定义时声明为协变这样会把 List[String]
作为
List[Any]
的子类型。
18.4.2 应用实例
在这里引入关于这个符号的说明,在声明
Scala
的泛型类型时,“
+
”表示协变,而“
-
”表示逆变
C[+T]
:如果
A
是
B
的子类,那么
C[A]
是
C[B]
的子类,称为协变
C[-T]
:如果
A
是
B
的子类,那么
C[B]
是
C[A]
的子类,称为逆变
C[T]
:无论
A
和
B
是什么关系,
C[A]
和
C[B]
没有从属关系。称为不变
.
代码
object Demo {
def main(args: Array[String]): Unit = {
val t1: Temp3[Sub] = new Temp3[Sub]("hello");
//ok
// val t2: Temp3[Sub] = new Temp3[Super]("hello");//error
// val t3: Temp3[Super] = new Temp3[Sub]("hello");//error
val t4: Temp3[Sub] = new Temp3[Sub]("hello"); //ok
val t5: Temp4[Super] = new Temp4[Sub]("hello"); //ok
//val t6: Temp4[Sub] = new Temp4[Super]("hello"); //ok
val t7: Temp5[Sub] = new Temp5[Sub]("hello"); //ok
val t8: Temp5[Sub] = new Temp5[Super]("hello"); //ok
//val t9: Temp5[Super] = new Temp5[Sub]("hello"); //ok
}
}
//协变
class Temp4[+A](title: String) { //Temp3[+A] //Temp[-A]
override def toString: String = {
title
}
}
//逆变
class Temp5[-A](title: String) { //Temp3[+A] //Temp[-A]
override def toString: String = {
title
}
}
//不变
class Temp3[A](title: String) { //Temp3[+A] //Temp[-A]
override def toString: String = {
title
}
}
//支持协变
class Super //父类
//Sub是Super的子类
class Sub extends Super