Scala语言学习笔记——泛型、上下界、视图界定、上下文界定、协变逆变不变、闭包、柯里化

本文围绕Scala展开,介绍了泛型应用案例,详细讲解类型约束,包括上界、下界、视图界定和上下文界定的使用及特点,还阐述了协变、逆变和不变的概念与应用,此外,对Scala中的闭包和参数柯里化也进行了说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.Scala泛型

用案例1

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestFanXing {
  def main(args: Array[String]): Unit = {
    val strMes = new StrMessage[String]("10")
    println(strMes.get)
    val intMes = new IntMessage[Int](20)
    println(intMes.get)
  }
}
  //在Scala定义泛型用[T],s为泛型的引用
  abstract class Message[T](s: T) {
    def get: T = s
  }
  //子类扩展的时候,约定了具体的类型
  class StrMessage[String](msg: String) extends Message(msg)
  class IntMessage[Int](msg: Int) extends Message(msg)

用案例2

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestFanXing {
  def main(args: Array[String]): Unit = {
    val class1 = new EnglishClass[SeasonEm, String, String](SeasonEm.spring, "001班", "高级班")
    println(class1.classSeason + " " + class1.className + " " + class1.classType)
    val class2 = new EnglishClass[SeasonEm, String, Int](SeasonEm.spring, "002班", 1)
    println(class2.classSeason + " " + class2.className + " " + class2.classType)
  }}
//Scala枚举类型
object SeasonEm extends Enumeration {
  type SeasonEm = Value //自定义SeasonEm,是Value类型,这样才能使用
  val spring, summer, winter, autumn = Value
}
//定义一个泛型类
class EnglishClass[A, B, C](val classSeason: A, val className: B, val classType: C)

2.型约束-(Upper Bounds)/下界(lower bounds)

(Upper Bounds)绍和使用

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

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

在 Scala 表示某个类型是 A 类型的子类型,也称上界上限,使用 <: 关键字,语法如下:[T <: A]   或用通配符:  [ _ <: A ]

应用案例

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestFanXing {
  def main(args: Array[String]): Unit = {
    val compareComm4 = new CompareComm[java.lang.Float](201.9f, 30.1f)
    println(compareComm4.greater)
  }
}
//使用上界的方式,可以有更好的通用性
class CompareComm[T <: Comparable[T]](obj1: T, obj2: T) {
  def greater = if(obj1.compareTo(obj2) > 0) obj1 else obj2
}

上界小结:上界形式如 U <: T,表示U必须是T类型的子类,换句话讲,传入的参数类型U最顶层最大也只能是T类型本身,就类似于数学中的 n <= 1 ,所以称之为上界。当然在U和T之间是存在直接或者间接的继承关系的,在Scala上界中,这里的U只能传入T的子类或者T类型本身,如果传入T的父类,则直接编译器报语法错误!

 (Lower Bounds)绍和使用

在 Java 泛型里表示某个类型是 A型的父类型,使用 super 关键字: <T super A>  或用通配符的形式:<? super A>

在 scala 下界或限,使用 >: 关键字,语法如下:[T >: A]  或用通配符:[ _ >: A ]

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestFanXing {
  def main(args: Array[String]): Unit = {
    biophony(Seq(new Earth, new Earth)).map(_.sound())
    biophony(Seq(new Animals, new Animals)).map(_.sound())
    biophony(Seq(new Bird, new Bird)).map(_.sound())

    val res1 = biophony(Seq(new Bird))
    val res2 = biophony(Seq(new Object))
    val res3 = biophony(Seq(new Moon))
    println("\nres2=" + res1)
    println("\nres2=" + res2)
    println("\nres3=" + res2)
  }
  def biophony[T >: Animals](things: Seq[T]) = things
}
class Moon {}
class Earth { //Earth 类
  def sound() { //方法
    println("hello Earth!")
  }
}
class Animals extends Earth {
  override def sound() = { //重写了Earth的方法sound()
    println("animal sound")
  }
}
class Bird extends Animals {
  override def sound() = { //将Animal的方法重写
    println("bird sounds")
  }
}

  

问题:上面案例中的 biophony(Seq(new Earth, new Earth)).map(_.sound()) 和 biophony(Seq(new Animals, new Animals)).map(_.sound()),我们可以理解,因为Earth和Animals是 [T >: Animals] 是其父类和本身,biophony(Seq(new Bird, new Bird)).map(_.sound()) 而为什么Bird类明明是Animals的子类而不是Animals的父类,按我们的理解,这里只能传入Animals的超类,为什么编译器不报错?为什么运行还成功了?
     我们通过Scala终端窗口进行测试发现,当我们传入Animals的父类或者本类,Scala是按照其父类或本类进行处理。如下图所示,我们可以发现,其Seq序列的泛型类型为父类或者本类,其List中存储的类型也是Earth或Animals对象类型

 

    但是当我们传入Animals的子类,其底层返回的是Bird父类Animals的引用类型,但是其List列表中实际存储的还是Bird类型,也解决我们从结果中的疑惑,不仅编译没有报错,并且结果还是调用的Bird类的sound()方法。

 

    那如果我们在下界中传入一个与Animals无任何继承关系的类,编译会不会报错?能不能正常运行呢? 

 

     通过测试我们发现,传入一个无关的类型Moon,Scala会将其当成Object类型处理。

Scala中下界的使用小结

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

 2)入和Animal有继承关系的类型,如果是Animal父类则按照父类处理,如果是Animal子类的按照Animal类型理,如果和Animal无关的,一律按照Object处理

 3)下界,可以随便传,只是处理是方式不一样,不能使用上界的思路来类推下界的含义

3.型约束-图界(View bounds)

视图界定基本介绍

   <% 的意思是“view bounds”(视界),它比 <: 适用的范围更广,除了所有的子类型,还允许隐式转换类

  def method [A <% B](arglist): R = ...  等价于:def method [A](arglist)(implicit viewAB: A => B): R = ...  或等价于:implicit def conver(a:A): B = …

  <% 除了方法使用之外,class 声明类型参数时也可使用:class A[T <% Int]

视图界定应用案例1

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestFanXing {
  def main(args: Array[String]): Unit = {
    val compareComm = new CompareComm(20, 30)
    println(compareComm1.greater)
  }
}
class CompareComm[T <% Comparable[T]](obj1: T, obj2: T) {
  def greater = if(obj1.compareTo(obj2) > 0) obj1 else obj2
}

视图界定应用案例2

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestFanXing {
  def main(args: Array[String]): Unit = {
    implicit def personalOrdered(person: Personal) = new Ordered[Personal]{
      override def compare(that: Personal): Int = person.age - that.age
    }
    val p1 = new Personal("tom", 10)
    val p2 = new Personal("jack", 20)
    val compareComm = new CompareComm(p1, p2)
    println(compareComm.getter)
  }
}
class Personal(val name: String, val age: Int) extends Ordered[Personal] {
  override def toString: String = this.name + "\t" + this.age
  override def compare(that: Personal): Int = this.age - that.age
}
class CompareComm[T <% Ordered[T]](obj1: T, obj2: T) {
  def getter = if (obj1 > obj2) obj1 else obj2
}

  注意:上面这个案例,<% 视图界定方式比上界更加灵活,并且可以使用隐式转换!

4.型约束-上下文界定(Context bounds)

    视图界定 T<%V 要求必须存在一个从T到V的隐式转换。上下文界定的形式为T:M,其中M是另一个泛型类,它要求必须存在一个类型为M[T]的 "隐形值",例如:class Pair[T: Ordering],上述定义要求必须存在一个类型为Ordering[T]的隐式值。 该隐式值可以被用在该类的方法中,当你声明一个使用隐式值的方法时,需要添加一个"隐式参数"。

应用案例

class Pair[T:Ordering](val first:T,val second : T){
  def smaller(implicit ord: Ordering[T])=
    if(ord.compare(first,second)<0) first else second
}

5.协变、逆变和不变

① Scala的协变(+),逆变(-),协covariant、逆变contravariant、不可变invariant

② 在Java里,泛型类型都是invariant,比如 List<String> 并不是 List<Object> 的子类型。而Scala支持,可以在定义类型时声明(用加号表示为协变,减号表示逆变),如trait List[+T] // 在类型定义时声明为协变,这样会把List[String]作为List[Any]的子类型

 对于一个带类型参数的类型,比如:List[T],如果B是A的子类,满足 List[B]也符合List[A]的子类型,那么就称为covariance(协变) ,如果 List[A]List[B]的子类型,即与原来的父子关系正相反,则称为contravariance(逆变)果一个类型支持协变或逆变,则称这个类型为variance(翻译为可变的或变型),否则称为 invariance(不可变的)。

应用实例

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

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

   C[-T]:如果AB的子类,那么C[B]C[A]的子类,称为逆 

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

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestVariant {
  def main(args: Array[String]): Unit = {
    val t: Temp[Super] = new Temp[Sub]("hello world")
  }
}
//支持协变
class Super
class Sub extends Super
class Temp[+A](title: String) {
  override def toString: String = {
    title
  }
}

6.Scala中的闭包

   闭包,和js中的闭包一样,返回值依赖于声明在函数外部的一个或多个变量,那么这个函数就是闭包函数。

val i: Int = 20
//函数func的方法体中使用了在func外部定义的变量 那func就是个闭包函数
val func = (x: Int) => x + i

7.Scala中参数的柯里化

    柯里化(Currying):指的是把原来接受多个参数的函数变换成接受一个参数的函数过程,并且返回接受余下的参数且返回结果为一个新函数的技术。柯里化并不是Scala特有的,js中也有。

/**
  * @author huleikai
  * @create 2019-05-27 11:23
  */
object TestCurrying {
  def main(args: Array[String]): Unit = {
    //原本的方法
    def m(x: Int, y: Int) = x + y
    //第一种柯里化
    def first(x: Int) = (y: Int) => x + y

    val second = first(1)
    val result: Int = second(2)
    println(result)
    //第二种柯里化
    def curriedSum(x: Int)(y: Int) = x + y

    val sum: Int = curriedSum(1)(2)
    println(sum)
  }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值