1、 泛型
本质是参数化类型,
(1) Array可以取泛型参数及类型变量做其元素的类型。
(2) 对基本类型的支持
(3) 声明地点可变性
(4) 对于上下界的支持,以及将多个上界作为复合类型模式的安排。
import scala.collection.immutable.Queue
class Triple[F,S,T](val first:F,val second:S,val third:T)
object HelloTypeParameterization{
def main(args:Array[String]){
val triple=new Triple(“spark”,3,3.1415)
val bigdata=new Triple[String,String,Char](“Spark”,”Hadoop”,”R”)
val getDate[T](list:List[T])=list(list.length/2)
println(getData(List(“Spark”,”Hadoop”,”R”)))
val f=getData[Int]_
println(f(List(1,2,3,4,5)))
val queue=Queue(1,2,3,4)
}
}
2、 界定
java范类中的上下界界定。
类型上界或
class User(val userName: String,val age: Int) extends Comparable[User] {
override def compareTo(o: User): Int = this.age - o.age
}
object OrderingDemo extends App {
/**
* 类型界定,上界
*
* @param u1
* @param u2
* @tparam T T必须是Comparable[T]的子类
* @return
*/
def compare[T <: Comparable[T]](u1: T, u2: T): Int = {
if (u1.compareTo(u2) == 0) 0 else if (u1.compareTo(u2) > 0) 1 else -1
}
val u1 = new User("u1", 18)
val u2 = new User("u2", 19)
// compare(u1,u2) //class User(userName: String, age: Int)的话,编译不通过
println(compare(u1,u2)) //class User(val userName: String,val age: Int) extends Comparable[User]
}
上界
trait Similar {
def isSimilar(x: Any): Boolean
}
case class MyInt(x: Int) extends Similar {
def isSimilar(m: Any): Boolean =
m.isInstanceOf[MyInt] &&
m.asInstanceOf[MyInt].x == x
}
object UpperBoundTest extends App {
def findSimilar[T <: Similar](e: T, xs: List[T]): Boolean =
if (xs.isEmpty) false
else if (e.isSimilar(xs.head)) true
else findSimilar[T](e, xs.tail)
val list: List[MyInt] = List(MyInt(1), MyInt(2), MyInt(3))
println(findSimilar[MyInt](MyInt(4), list))
println(findSimilar[MyInt](MyInt(2), list))
}
下界
case class ListNode[+T](h: T, t: ListNode[T]) {
def head: T = h
def tail: ListNode[T] = t
def prepend[U >: T](elem: U): ListNode[U] =
ListNode(elem, this)
}
object LowerBoundTest extends App {
val empty: ListNode[Null] = ListNode(null, null)
val strList: ListNode[String] = empty.prepend("hello")
.prepend("world")
val anyList: ListNode[Any] = strList.prepend(12345)
}
3、 类型约束
类型等同约束 T=:=U 测试T类型是否等同于U类型
子类型约束 T<:
object TypeContrains{
def main(args:Array[String]){
//A=:=B
//A<:
def rocky[T](i:T)(implicit ev:T<:is short, you need Spark!”)
}
rocky(“Spark”)
}
}
4、 类型系统
ClassTag特质用于存储被擦除的T类型的类信息,运行时的类信息,
TypeTag特质包涵T所有的静态类型信息
class A[T]
object Manifest_ClassTag{
def main(args:Array[String]){
def arrayMake[T:Manifest](first:T,second:T)={
val r=new Array[T](2);
r(0)=first;
r(1)=second;
r
}
arrayMake(1,2).foreach(println)
def mkArray[T:ClassTag](elems:T*)=Array[T](elems:_*)
mkArray(42,31).foreach(println)
mkArray(“Hello”,”Spark”,”and you”).foreach(println)
def manif[T](x:List[T])(implicit m:Manifest[T])={
if(m<:<manif[String])
println(“List String”)
else
println(“Other type”)
}
val m=manifest[A[String]]
println(m)
val cm=classManifest[A[String]]
println(m)
}
}
5、 型变Variance
scala型变注释是在定义类型抽象时指定,
先说说协变和逆变(实际上还有非变)。协变和逆变主要是用来解决参数化类型的泛化问题。由于参数化类型的参数(参数类型)是可变的,当两个参数化类型的参数是继承关系(可泛化),那被参数化的类型是否也可以泛化呢?Java中这种情况下是不可泛化的,然而Scala提供了三个选择,即协变、逆变和非变。下面说一下三种情况的含义,首先假设有参数化特征Queue,那它可以有如下三种定义。
1)trait Queue[T] {}
这是非变情况。这种情况下,当类型S是类型A的子类型,则Queue[S]不可认为是Queue[A]的子类型或父类型,这种情况是和Java一样的。
2)trait Queue[+T] {}
这是协变情况。这种情况下,当类型S是类型A的子类型,则Queue[S]也可以认为是Queue[A}的子类型,即Queue[S]可以泛化为Queue[A]。也就是被参数化类型的泛化方向与参数类型的方向是一致的,所以称为协变。
3)trait Queue[-T] {}
这是逆变情况。这种情况下,当类型S是类型A的子类型,则Queue[A]反过来可以认为是Queue[S}的子类型。也就是被参数化类型的泛化方向与参数类型的方向是相反的,所以称为逆变。
接着看一个例子。
trait Function[-Arg,+Return]{
def apply(arg:Arg):Return
} //defined trait Function
val foo = new Function[Any,String]{
override def apply(arg:Any) :String = s"Hello ,I received a $arg"
} //foo: Function[Any,String] = $anon$1@72dda13a
val bar:Function[String,Any] = foo
//bar: Function[String,Any] = $anon$1@72dda13a
foo("test") //res0: String = Hello ,I received a test
bar("test") //res1: Any = Hello ,I received a test
1
首先我们看一下Function的定义,在参数Arg位置被定义成了逆变,这个意味着我们可以在需要某个类型的时候用他的父类型替换,也就是老子换儿子,在返回值类型被定义成了协变,这就是在需要某个类型的时候用他的子类型替换,白话就是儿子换老子
2
我们看一下foo的定义,他的参数类型是Any,返回值类型是String,所以他的apply方法接收一个Any类型的参数,返回一个String类型的结果,这就是为什么foo(“test”)返回的是String类型的res0
3
我们再看一下bar的定义,参数类型为String,返回值类型为Any,所以如果能调用bar,需要传入String,结果是Any,也就是bar(“test”)是Any的原因
4
那么为什么可以写那句val bar:Function[String,Any] = foo
这就和型变有关了,由于Function参数位置逆变,所以需要String的时候我们能用他的老子Any替换,foo类型的参数位置正好是Any,同理返回值类型协变,需要的是Any,我们传入String自然也没错,所以可以通过编译并产生结果