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]:如果A是B的子类,那么C[A]是C[B]的子类,称为协变
C[-T]:如果A是B的子类,那么C[B]是C[A]的子类,称为逆变
C[T]:无论A和B是什么关系,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)
}
}