类型

Posted on 2017-12-18 11:19  打杂滴  阅读(270)  评论(0)    收藏  举报

类型可以让你表示函数的域和值域。例如,在数学里,我们经常看到下面的函数:f: R -> N

这个定义告诉我们函数”f”的作用是把实数集里的数映射到自然集里。

cala强大的类型系统让我们可以使用更具有表现力的表达式。一些主要的特点如下:

  • 支持参数多态,泛型编程
  • 支持(局部)类型推导,这就是你为什么不需要写val i: Int = 12: Int
  • 支持存在向量(existential quantification),给一些没有名称的类型定义一些操作
  • 支持视图

 

Scala的类型系统必须同时解释类层次和多态性

Scala的类型系统需要把类的继承关系和多态结合起来。类的继承使得类之间存在父子的关系。

covariant(协变) C[T’]是C[T]的子类 [+T]
contravariant(逆变) C[T]是C[T’]子类 [-T]
invariant(不变) C[T]和C[T’]不相关 [T]

 

 

 

边界

下例中 A 必须“可被视”为 Long

scala> class Container[A <% Long] { def addIt(x: A) = 123 + x }
defined class Container

scala> val con=new Container[Int]
con: Container[Int] = Container@6954f50c

scala> con.addIt("100")
<console>:13: error: type mismatch;
found : String("100")
required: Int
con.addIt("100")
^

scala> con.addIt(100)
res21: Long = 223

scala> val con=new Container[Long]
con: Container[Long] = Container@28b2ca91

scala> con.addIt(100)
res22: Long = 223

----------------------------------

A =:= B A 必须和 B相等

 

scala> class Container[A](v: A) { def addIt(implicit evidence: A =:= Int) = 521 + v }
defined class Container

scala> (new Container[Int](400)).addIt
res30: Int = 921

scala> (new Container[Long](400)).addIt
<console>:13: error: Cannot prove that Long =:= Int.
(new Container[Long](400)).addIt
^

A <:< B A 必须是 B的子类

 


scala> class Container[A](v: A) { def addIt(implicit evidence: A <:< Long) = 521 + v }
defined class Container

scala> (new Container[Short](400)).addIt
<console>:13: error: Cannot prove that Short <:< Long.
(new Container[Short](400)).addIt
^

scala> (new Container[Long](400)).addIt
res7: Long = 921

 

Scala允许你通过 边界 来限制多态变量。这些边界表达了子类型关系。

scala类型系统相对复杂,自己提供了一个scala.reflect.runtime.universe.Type

可以通过typeOf获取类型信息

scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._

scala> class person{val name="tianyongtao";def printstr(str:String){println(str)}}
defined class person

scala> typeOf[person]
res97: reflect.runtime.universe.Type = person

也可以通过classOf获取类的信息

scala> class person{val name="tianyongtao";def printstr(str:String){println(str)}}
defined class person

scala> classOf[person]
res96: Class[person] = class person

typeOf 和 classOf 方法接收的都是类型符号(symbol),并不是对象实例

scala> val p1=new person
p1: person = person@35808a96

scala> typeOf[p1]
<console>:18: error: not found: type p1
typeOf[p1]
^

scala> classOf[p1]
<console>:18: error: not found: type p1
classOf[p1]
^

对于实例,要获取他的 Class 信息,可以通过 getClass 方法

scala> p1.getClass
res100: Class[_ <: person] = class person

scala> object ob
defined object ob

scala> ob.getClass
res108: Class[_ <: ob.type] = class ob$

上例中对应ob对应的class不是ob而是ob$,ob.type看上去像是这个单例内部的一个成员,用这个成员的值表示其类型;

实际上.type之前的都可以看做是一种类型路径,这种特殊的类型也叫单例类型,它是面向对象实例的,每个实例都可以通过.type方式表达它的单例类型

 


scala> class person{class man}
defined class person

 

scala> val p1=new person
p1: person = person@12fac545

 

scala> val p2=new person
p2: person = person@3f624491

 

scala> val m1=new p1.man
m1: p1.man = person$man@263b6a1f

 

scala> val m2=new p2.man
m2: p2.man = person$man@46403498

 

scala> m1.getClass
res111: Class[_ <: p1.man] = class person$man

 

scala> m2.getClass
res112: Class[_ <: p2.man] = class person$man

scala> val m11=new p1.man
m11: p1.man = person$man@24e4ee67

scala> m11.getClass
res117: Class[_ <: p1.man] = class person$man

上例中,实例m1,m2的类都是class person$man,但他们的类型不同

这是因为内部类型依赖外部实例,外部实例不同,他们也不同。

因此,实例m1,m11的类型是相同的
scala> typeOf[p1.man]
res114: reflect.runtime.universe.Type = p1.man

scala> typeOf[p2.man]
res115: reflect.runtime.universe.Type = p2.man

scala> typeOf[p2.man]==typeOf[p1.man]
res116: Boolean = false

 

 简单的说,类(class)与类型(type)是两个不一样的概念,类型(type)比类(class)更”具体”,任何数据都有类型。

类是面向对象系统里对同一类数据的抽象,在没有泛型之前,类型系统不存在高阶概念,直接与类一一映射,而泛型出现之后,就不在一一映射了。

比如List[Int],List[String]他们同属于类List,但类型根据不同的构造参数而不同

单例类型(singleton type)

singleton type是所有实例都可以有的

scala> object a
defined object a

scala> a.getClass
res120: Class[_ <: a.type] = class a$

scala> typeOf[a.type]
res124: reflect.runtime.universe.Type = a.type

上述例中单例类型,它的类型和它的类不同,要用a.type表示


scala> val atmp:a.type=a
atmp: a.type = a$@13061bfe

scala> atmp
res125: a.type = a$@13061bfe

单例是唯一的,a.type类型的实例只有唯一的实例a

scala> class person{class man}
defined class person

scala> val p1=new person
p1: person = person@14df0b7b

scala> typeOf[p1.type]
res128: reflect.runtime.universe.Type = p1.type

类型声明一个变量看看:

scala> val p11:p1.type=p1
p11: p1.type = person@14df0b7b

所有的对象实例都有一个x.type的单例类型,它只对应当前对象实例。

scala> class man{def setsex(sex:String)={println(sex);this};def setage(age:Int)={println(age);this}}
defined class man

scala> val m1=new man
m1: man = man@1719c8f9

scala> m1.setsex("man").setage(20)
man
20

如果存在子类的话:

scala> class man extends person{def hobby(it:String)={println("football");this}}
defined class man

scala> val m1=new man
m1: man = man@6b1118e4

scala> m1.hobby("aaa").setage(20).setsex("man")
football
20
man

?????以后继续研究

 

scala里的类型,除了在定义class,trait,object时会产生类型,还可以通过type关键字来声明类型。

结构类型(structural type)为静态语言增加了部分动态特性,使得参数类型不再拘泥于某个已命名的类型,只要参数中包含结构中声明的方法或值即可。

  结构类型:一组关于抽象的方法,字段,类型的规格说明

Scala 支持 结构类型 structural types — 类型需求由接口 构造 表示,而不是由具体的类型表示。

scala> import scala.language.reflectiveCalls
import scala.language.reflectiveCalls

scala> def foo(x: { def get: Int }) = 123 + x.get
foo: (x: AnyRef{def get: Int})Int

scala> foo(new {def get=10})
res9: Int = 133

 

抽象类型成员

在特质中,你可以让类型成员保持抽象。

scala> trait Foo { type A; val x: A; def getX: A = x }
defined trait Foo

scala> (new Foo{type A=Int;val x=100}).getX
res20: Int = 100

scala> (new Foo{type A=String;val x="hello world"}).getX
res23: String = hello world

您可以使用hash操作符来引用一个抽象类型的变量:


scala> trait Foo[M[_]] { type t[A] = M[A] }
defined trait Foo

scala> val x: Foo[List]#t[Int] = List(1)
x: List[Int] = List(1)

scala> val x: Foo[List]#t[Int] = List(1,2,5)
x: List[Int] = List(1, 2, 5)

 

type相当于声明一个类型别名

scala> type str=String
defined type alias str

scala> val aa:str="hello"
aa: str = hello

scala> typeOf[aa.type]
res156: reflect.runtime.universe.Type = aa.type

scala> typeOf[str]
res157: reflect.runtime.universe.Type = str

上面把String类型用str代替,通常type用于声明某种复杂类型,或用于定义一个抽象类型。

 

 

函数类型

函数类型写法:

(T1,T2…) => R

小括号里的是入参类型(最多可以有22个,最少为0),右箭头右边的是返回结果类型。

this别名&自身类型

看scala的源码的话很发现很多源码开头都有一句:self => 这句相当于给this起了一个别名为self

scala> class person{ self => ;val age=100;def printage(){println(self.age)} }
defined class person

scala> val p1=new person
p1: person = person@2e810089

scala> p1.printage
100

------

scala> class person{ val age=100;def printage(){println(this.age)} }
defined class person

scala> val p1=new person
p1: person = person@7e2454e5

scala> p1.printage
100

---------------
scala> class person{pers=>;val age=100;class man{ ma=> ;val age=50;def avg()={(pers.age+ma.age)/2}}}
defined class person

scala> val p2=new person
p2: person = person@300b752f

scala> val man=new p2.man
man: p2.man = person$man@421e00b7

scala> man.avg
res27: Int = 75

 

upper bounds(中文为上限或上界)

upper bounds适用于把泛型对象当作数据的提供者(生产者)的场景下

写法如下:

[T <: Test] 或 [_ <: Test]

scala> def printlst(lst:List[_<:Any]){lst.foreach(print)}
printlst: (lst: List[_])Unit

scala> printlst(List("tian","yongt","tao",50))
tianyongttao50

 

lower bounds(中文为下限或下界)

lower bound适用于把泛型对象当作数据的消费者的场景下

写法如下:

[T >: Test] 或 [_ >: Test]

scala> def add[T>:String](arr:ArrayBuffer[T],itm:String)={arr.append(itm);arr}
add: [T >: String](arr: scala.collection.mutable.ArrayBuffer[T], itm: String)scala.collection.mutable.ArrayBuffer[T]

scala> add(ArrayBuffer(),"50")
res32: scala.collection.mutable.ArrayBuffer[String] = ArrayBuffer(50)

 

Scala里对上界和下界不能有多个,不过变通的做法是使用复合类型(compund type):

[T <: A with B]

[T >: A with B]

 

multiple bounds 复合类型

T <: A with B    

T >: A with B

T >: A <: B   //下界必须写前面,上界写后面,A要符合B的子类型

T <% A <% B // 必须同时存在 T=>A的隐式转换,和T=>B的隐式转换。

T : A : B // 必须同时存在A[T]类型的隐式值,和B[T]类型的隐式值。

 

博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3