• Scala学习十七——类型参数


    一.本章要点

    • 类、特质、方法和函数都可以有类型参数
    • 将类型参数放置在名称之后,以方括号括起来
    • 类型界定的语法为T<:UpperBound、T>:LowerBound、T<%ViewBound、T:ContextBound
    • 可以用类型约束来约束一个方法,比如(implicit ev:T<:<UpperBound)
    • 用+T(协变来表示某个泛型的子类型关系和参数T方向一致,或用-T(逆变)来表示方向相反
    • 协变适用于表示输出的类型参数,比如不可表集合中的元素
    • 逆变适用于表示输入的类型参数,比如函数参数

    二.泛型类

      和Java/C++一致,类和特质都可以带类型参数。在类的定义中,可以用类型参数来定义变量、方法参数、以及返回值类型。例:

    class Pair[T,S]{val first:T,val secend:S}

    三.泛型函数

      函数和方法也可以带类型参数。例:

    def getMiddle[T](a:Array[T])=a(a.length/2)

    四.类型变量界定

      对类型变量进行限制,例:

    //错误
    class Pair[T](val first:T,val secend:T)
    //不知道first是否有compareTo方法
    {def smaller=if(first.compareTo(secend)<0) first else secend
    }
    
    //解决方法(比较简单,也可以指定一个下界)
    class Pair[T<:Comparable[T]](val first:T,val secend:T){
    def smaller=if(first.compareTo(secend)<0) first else secend
    }  
      

    五.视图界定

      ”视图界定“,T<%V要求必须存在一个从T到V的隐式转换,如class Pair[T <% Comparable[T]],<%意味着T可以被隐式转换成Comparable[T]。。。          

    六.上下文界定

      格式为T:M,其中M是一个泛型类,要求必须存在一个类型为M[T]的”隐式值“。

        

    七.Manifest上下文界定

      要实例化一个泛型的Array[T],需要一个Mainfest[T]对象(要想让基本类型的数组能工作,这是必须的),例:

    def makePair(T:Mainfest)(first:T,secend:T){
    val r=new Array[T](2);r(0)=first;r(1)=secend;r
    }

      注:如果T是Int,希望虚拟机中对应的是一个int[]数组,在Scala中,Array只不过是类库中提供的一个类,编译器并不对它做特殊处理,因此要编写一个泛型函数来构造泛型数组,需要传入Mainfest对象帮忙(因为在虚拟机中,泛型相关的信息是被抹掉的,只会有一个makePair方法,却要处理所有类型T)。

    八.多重界定

      类型变量可以同时有上界和下界,写法为T >:Lower <:Upper,不能同时有多个上界或多个下界,但能同时实现多个特质。

      可以有多个视图特质:T%<Comparable[T]<%String

      可以有多个上下文界定:T:Ordering:Mainfest

    九.类型约束

      提供的另一种限定类型的方式,总共有三种关系:

        T=:=U

        T<:<U

        T<%<U

      这些约束将会测试T是否等于U,是否是U的子类型,或是否能被视图(隐式)转换为U,要使用这样的约束,需要添加”隐式类型证明参数“。  

      类型参数可在泛型类中定义只能在特定条件下使用的方法,例:

    class Pair[T](val first:T,val second:T){
    def smaller(implicit ev:T<:<Ordered[T])=
    if(first<second) first else second
    }

      类型约束的另一个用途是改进类型推断,例:

    def firstLast[A,C<:Iterable[A](it:C)=(it.head,it.last)]
    
    //执行,推断出的类型参数[Nothing,List[Int]]不符合[A,C<:Iterable[A]]
    firstLast(List(1,2,3))  

    十.型变

      例:

    def makeFriends(p:Pair[Person])
    //如果用Pair[Student]作为参数调用makeFriends在缺省情况下是错误的,尽管Student是Person的子类,但Pair[Student]和Pair[Person]没有任何关系
    
    //想要这样的关系,必须在Pair类中表明
    //+号意味着该类型是T协变的(与T按同样的类型型变),即Student是Person的子类型=》Pait[Student]是Pair[Person]的子类型
    class Pair[+T](val first:T,val second:T)
    
    //另一个方向的型变
    trait Friend[-T]{
    def befriend(someone:T)}
    //可以是逆变的,Student是Person的子类型,Friend[Student]是Friend[Person]的超类型
    class Person extends Friends[Person]
    class Student extends Person
    val susan=new Student
    val fred=new Person
    makeFriendWith(susan,fred)
    //注:在一个泛型的类型声明里面,可以同时使用这两种型变

    十一.协变和逆变点

      通常而言,对于某个对象消费的值适用于逆变,而对于它产出的值则适用于协变。

      在Scala中数组是不支持型变的,不能将Array[Student]转换为Array[Person],或者反过来会不安全。。。        

    十二.对象不能泛型

      没法给对象添加类型参数,比如可变列表。。。

    十三.类型通配符

      在Java中,所有泛型类都是不可变的,可以使用通配符改变它们的类型。

      可以在Scala中使用通配符。。。  

    十四.练习

  • 相关阅读:
    pytorch 深度学习之数据操作
    pytorch 深度学习之微积分
    pytorch 深度学习之线性代数
    pytorch 深度学习之自动微分
    [Photoshop] ps中选区新建为图层的快捷键
    [Golang] Goland 编辑器 替换快捷键
    Go 自定义 Json 序列化规则
    MySQL入门篇(六)之mysqldump备份和恢复
    go panic 和 recover(重要)
    如何在 Linux 上模拟和缓解 DDoS 攻
  • 原文地址:https://www.cnblogs.com/lyq-biu/p/11978289.html
Copyright © 2020-2023  润新知