17.1 泛型的基本介绍

  17.1.1 基本介绍

      1) 如果我们要求函数的参数可以接受任意类型,可以使用泛型,这个类型可以代表任意的数据类型

      2) 例如List,在创建List时,可以传入整型、字符串、浮点数等等任意类型。那是因为List在类定义时引用了泛型。比如在Java中:public interface List<E> extends Collection<E>

  17.1.2 泛型的应用案例1

      -要求

        1) 编写一个Message类

        2) 可以构建Int类型的Message,String类型的Message

        3) 要求使用泛型来完成设计(说明:不能使用Any)

      -案例演示

object boke_demo01 {

  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)

  17.1.3 泛型的应用案例2

      -要求

        1) 定义一个函数,可以获取各种类型的List的中间index的值

        2) 使用泛型完成

      -案例演示

object boke_demo01 {

  def main(args: Array[String]): Unit = {
val list1 = List("hello", "Tom", "world")
val list2 = List(90, 10, 23)
println(midList[String](list1)) // "Tom"
println(midList[Int](list2)) // 10
} /*
要求
定义一个函数,可以获取各种类型的 List 的中间index的值
使用泛型完成 */
def midList[E](l: List[E]): E = {
l(l.length / 2)
}
}

17.2 类型约束-上界(Upper Bounds)/下界(Lower Bounds)

  17.2.1 上界(Upper Bounds)介绍和使用

      -Java中上界

        在Java泛型里表示某个类型是A类型的子类型,使用extends关键字,这种形式叫upper bounds(上限或上界),语法如下:

<T extends A>
//或用通配符的形式:
<? extends A>

      -Scala中上界

        在Scala里表示某个类型是A类型的子类型,也称上界或上限,使用 <: 关键字,语法如下:

[T<:A]
//或用通配符:
[_ <: A]

      -Scala中上界应用案例-要求

        1) 编写一个通用的类,可以进行Int之间、Folat之间,等实现了Comparable接口的值直接的比较.//java.lang.Integer

        2) 分别使用传统方法和上界的方式来完成,体会上界使用的好处

        3) 案例演示

object boke_demo01 {

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

    val compareInt = new CompareInt(10,40)
println(compareInt.greater) // 40 //第一个用法
val commonCompare1 = new CommonCompare(Integer.valueOf(12), Integer.valueOf(36))//Int
println(commonCompare1.greater)
//第二个用法
val commonCompare2 = new CommonCompare(java.lang.Float.valueOf(1.6f), java.lang.Float.valueOf(2.7f))//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](12.1f, 23.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
}

  17.2.2 下界(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 boke_demo01 { 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中下界的使用小结

        1) 对于下界,可以传入任意类型

        2) 传入和Animal直系的,是Animal父类的还是父类处理,是Animal子类的按照Animal处理

        3) 和Animal无关的,一律按照Object处理

        4) 也就是下界,可以随便传,只是处理方式不一样

        5) 不能使用上界的思路来类推下界的含义

  17.2.3 视图界定应用案例

      -说明:隐式转换结合视图界定的方式,比较两个Person对象的年龄大小

object boke_demo01 {

  def main(args: Array[String]): Unit = {
//使用了隐式转换
val compareComm1 = new CompareComm(7, 23)
println(compareComm1.greater) val compareComm2 = new CompareComm(Integer.valueOf(20), Integer.valueOf(30))
println(compareComm2.greater) //以前 <: 上界
val compareComm3 = new CompareComm[java.lang.Float](201.9f, 27.1f)
println(compareComm3.greater)
//上面的小数比较,在视图界定的情况下,就可以这样写了
//这里会进行隐式转换
val compareComm4 = new CompareComm(201.9f, 360.1f)
println(compareComm4.greater) }
} //说明
//1. T <% Comparable[T] 说明 T是 Comparable子类型
//2. T <% Comparable[T] 和 T <: Comparable[T] 区别就是视图界定支持隐式转换
//3. 视图界定不但支持以前上界的写法,同时支持简洁的写法val compareComm1 = new CompareComm(1, 20) class CompareComm[T <% Comparable[T]](obj1: T, obj2: T) {
def greater = if (obj1.compareTo(obj2) > 0) obj1 else obj2
}

17.3 类型约束-上下文界定(Context bounds)

  17.3.1 基本介绍

      与view bounds一样context bounds(上下文界定)也是隐式参数的语法糖。为了语法上的方便,引用了“上下文这个概念”

  17.3.2 上下文界定应用实例

      -要求:使用上下文界定+隐式参数的方式,比较两个Person对象的年龄大小。使用Ordering实现比较

      -案例演示

object boke_demo01 {

  //这里我定义一个隐式值  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("Tom", 30)
val p2 = new Person4("Jack", 35)
val compareComm4 = new CompareComm4(p1, p2)
println(compareComm4.geatter) // "Jack", 35 val compareComm5 = new CompareComm5(p1, p2)
println(compareComm5.geatter) // "Jack", 35 println("personComparetor hashcode=" + personComparetor.hashCode())
val compareComm6 = new CompareComm6(p1, p2)
println(compareComm6.geatter) // "Jack", 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,就返回o2,否则返回o1
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
}
}

17.4 协变、逆变和不变

  17.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]的子类型

  17.4.2 应用实例

      -说明:在这里引入关于这个符号的说明,在声明Scala的泛型类型时,“+”表示协变,而“-”表示逆变

        1) C[+T]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变

        2) C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变

        3) C[T]:无论A和B是什么关系,C[A]和C[B]没有从属关系,称为不变

      -案例演示

object boke_demo01 {

  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[Super] = new Temp3[Super]("hello"); //ok
val t5: Temp4[Super] = new Temp4[Sub]("hello"); //ok
val t6: Temp4[Sub] = new Temp4[Super]("hello"); //error
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"); //error }
} //协变
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

      

17. Scala泛型、上下界、视图界定、上下文界定的更多相关文章

  1. 一文详解scala泛型及类型限定

    今天知识星球球友,微信问浪尖了一个spark源码阅读中的类型限定问题.这个在spark源码很多处出现,所以今天浪尖就整理一下scala类型限定的内容.希望对大家有帮助. scala类型参数要点 1. ...

  2. Scala 基础(十六):泛型、类型约束-上界(Upper Bounds)/下界(lower bounds)、视图界定(View bounds)、上下文界定(Context bounds)、协变、逆变和不变

    1 泛型 1)如果我们要求函数的参数可以接受任意类型.可以使用泛型,这个类型可以代表任意的数据类型. 2)例如 List,在创建 List 时,可以传入整型.字符串.浮点数等等任意类型.那是因为 Li ...

  3. Scala学习笔记--上界(<:)、视图界定(<%)、上下文界定(T:M)

    上界 下界 视界 object Test{ def main(args:Array[String]):Unit={ def mulBy(factor:Double)=(x:Double)=>fa ...

  4. Scala 上下文界定

    上下文界定的类型参数形式为T:M的形式,其中M是一个泛型,这种形式要求存在一个M[T]类型的隐式值: /** * 上下文界定 */ @Test def testOrdering_Class_Conte ...

  5. Scala泛型详解

    在Scala中你可以使用类型参数来实现类和函数,这样的类和函数可以用于多种类型.比如Array[T] 你可以存放任意指定类型T的数据. 类.特质.函数都可以有类型参数:将类型参数放在名字后面用方括号括 ...

  6. scala泛型

    package com.ming.test /** * scala泛型 * 类型参数测试 */ object TypeParamsTest { //泛型函数 def getMiddle[T](a:Ar ...

  7. ZOJ 3229 有上下界最大流

    1: /** 2: ZOJ 3229 有上下界的最大流 3: 两次求最大流的过程,非二分 4: 有源汇上下界的最大流问题, 首先连接 sink -> src, [0,INF]. 5: 根据net ...

  8. POJ 3801 有上下界最小流

    1: /** 2: POJ 3801 有上下界的最小流 3: 4: 1.对supersrc到supersink 求一次最大流,记为f1.(在有源汇的情况下,先使整个网络趋向必须边尽量满足的情况) 5: ...

  9. ZOJ 2314 带上下界的可行流

    对于无源汇问题,方法有两种. 1 从边的角度来处理. 新建超级源汇, 对于每一条有下界的边,x->y, 建立有向边 超级源->y ,容量为x->y下界,建立有向边 x-> 超级 ...

随机推荐

  1. python 生成 pyc 文件

    以 pyc 为扩展名的是Python的编译文件.其执行速度快于 py 文件且不能用文本编辑编辑查看.所以 pyc 文件往往代替 py 文件发布. Python 在执行时,首先会将 py 文件中的源代码 ...

  2. 从库延迟增大,MySQL日志出现InnoDB: page_cleaner: 1000ms intended loop took 17915ms.

    今天同事负责的数据库从库出现从库延迟增大,MySQL日志InnoDB: page_cleaner: 1000ms intended loop took 17915ms. 了解原因,keepalived ...

  3. LSTM和双向LSTM讲解及实践

    LSTM和双向LSTM讲解及实践 目录 RNN的长期依赖问题LSTM原理讲解双向LSTM原理讲解Keras实现LSTM和双向LSTM 一.RNN的长期依赖问题 在上篇文章中介绍的循环神经网络RNN在训 ...

  4. C#多线程和异步——Task和async/await详解

    阅读目录 一.什么是异步 二.Task介绍 1 Task创建和运行 2 Task的阻塞方法(Wait/WaitAll/WaitAny) 3 Task的延续操作(WhenAny/WhenAll/Cont ...

  5. Chaos Engineering 混沌工程 Chaos Monkey vs Chaos xxx vs Chaos Blade

    Chaos Engineering的历史.原则以及实践https://www.infoq.cn/article/chaos-engineering-the-history-principles-and ...

  6. python制作简单excel统计报表3之将mysql数据库中的数据导入excel模板并生成统计图

    python制作简单excel统计报表3之将mysql数据库中的数据导入excel模板并生成统计图 # coding=utf-8 from openpyxl import load_workbook ...

  7. Python3基础 keyword 查看所有的关键字

             Python : 3.7.3          OS : Ubuntu 18.04.2 LTS         IDE : pycharm-community-2019.1.3    ...

  8. GANomaly: Semi-Supervised Anomaly Detection via Adversarial Training-1-论文学习

    通过对抗训练实现半监督的异常检测 Abstract 异常检测在计算机视觉中是一个经典的问题,即从异常中确定正常,但是由于其他类(即异常类)的样本数量不足,所以数据集主要基于一个类(即正常类).虽然该问 ...

  9. 【Tomcat】Tomcat 原理架构(一)

    Tomcat是什么 开源的 Java Web 应用服务器,实现了 Java EE(Java Platform Enterprise Edition)的部 分技术规范,比如 Java Servlet.J ...

  10. Cheng MeiChun团队的技术支持

    本网页为Cheng MeiChun团队的技术支持网址,如果在我们开发的游戏中遇到任何问题,欢迎联系我们! QQ:1505478990 邮箱:1505478990@qq.com