scala 类型 的最详细解释
scala 是一个强类型的语言,但是在编程时可以省略对象的类型.
java中对象类型(type)与类(class)信息
jdk1.5 前 类型与类是一一映射,类一致类型就一致.
1.5 后引入了泛型,jvm 选择运行时擦除类型, 类型不可以只通过类信息进行判断.
比如: List<String>,List<Integer> 的class 都是 Class<List>,然而他们的类型是不相同的,泛型是需要通过反射来进行获得,
同时java通过增加 Type 来表达这种类型.
List<String> 的类型由 类构造器 和 类型参数 组成. 和 List<Integer> 完全不相同.
scala中类型
scala 没有用java自己的类型接口,使用 scala.reflect.runtime.universe.Type 接口
- 类获得类型或类信息
// 获取类型信息
import scala.reflect.runtime.universe._
class A
typeOf[A]
res44: reflect.runtime.universe.Type = A
// 获取类信息
classOf[A]
res52: Class[A] = class A
- 对象获得类信息
另外java 中对象获取类信息可以通过 getClass方法,scala继承了这个方法.
val a = new A
a.getClass
res53: Class[_ <: A] = class A
scala 类型与类区别
scala> class A { class B } // 嵌套类
scala> val a1 = new A
scala> val a2 = new A
scala> val b1 = new a1.B
scala> val b2 = new a2.B
scala> b1.getClass
res8: Class[_ <: a1.B] = class A$B
// 嵌套类的类信息是一致的是 A&B
scala> b1.getClass == b2.getClass
res7: Boolean = true
// 类型是不同的
scala> typeOf[a1.B] == typeOf[a2.B]
res10: Boolean = false
typeOf[a1.B]
res9: reflect.runtime.universe.Type = a1.B
类(class)与类型(type)是两个不一样的概念,类型(type)比类(class)更”具体”,任何数据都有类型。类是面向对象系统里对同一类数据的抽象,在没有泛型之前,类型系统不存在高阶概念,直接与类一一映射,而泛型出现之后,就不在一一映射了。比如定义class List[T] {}, 可以有List[Int] 和 List[String]等具体类型,它们的类是同一个List,但类型则根据不同的构造参数类型而不同。
类包括类型,类型一致他们的类信息也是一致的. 类型不一致,但是类可能一致,类型是所有编程语言都有的概念,一切数据都有类型。类更多存在于面向对象语言,非面向对象语言也有“结构体”等与之相似的概念;类是对数据的抽象,而类型则是对数据的”分类”,类型比类更“具体”,更“细”一些。
scala 内部依赖类型
class A{
| class B;
| def foo(b:B) = println(b)
| }
defined class A
scala> val a1 = new A
a1: A = A@34692767
scala> val a2 = new A
a2: A = A@d9c54cd
scala> val b1 = new a1.B
b1: a1.B = A$B@156aba9a
scala> val b2 = new a2.B
b2: a2.B = A$B@3829edd5
在java 中 内部类创建的对象是相同的,但是scala中b1 和 b2 是不同的类型.
a1.foo(b2)
<console>:17: error: type mismatch;
found : a2.B
required: a1.B
a1.foo(b2)
^
a1.foo方法接受的参数类型为:a1.B,而传入的b2 类型是 a2.B,两者不匹配。
路径依赖类型;比如上面的 A.this.B 就是一个路径依赖类型,B 前面的路径 A.this 随着不同的实例而不同,比如 a1 和 a2 就是两个不同的路径,所以a1.B 与 a2.B也是不同的类型。
- 内部类定义在Object中: 路径 package.object.Inner
- 内部类定义在class/trait 里:
class A {
class B
val b = new B // 相当于 A.this.B
}
类路径 A.B
class A { class B }
class C extends A { val x = new super.B } // 相当于 C.super.B
类路径 C.super.B
怎么让 a1.foo 方法可以接收 b2 参数 ?
类型投影(type projection)
在scala里,内部类型(排除定义在object内部的),想要表达所有的外部类A实例路径下的B类型,即对 a1.B 和 a2.B及所有的 an.B类型找一个共同的父类型,这就是类型投影,用 A#B的形式表示。
def foo(b: A#B)
结构类型
结构类型(structural type)为静态语言增加了部分动态特性,使得参数类型不再拘泥于某个已命名的类型,只要参数中包含结构中声明的方法或值即可。
def free( res: {def close():Unit} )
{
res.close
}
free(new { def close()=println("closed") })
closed
也可以使用Type定义别名
type X = { def close():Unit }
defined type alias X
def free(res:X) = res.close
free(new { def close()=println("closed") })
closed
如果某一类实现了close方法也可以直接传入
object A { def close() {println("A closed")} }
free(A)
A closed
结构类型还可以用在稍微复杂一点的“复合类型”中,比如:
scala> trait X1; trait X2;
scala> def test(x: X1 with X2 { def close():Unit } ) = x.close
上面声明test方法参数的类型为:
X1 with X2 { def close():Unit }
表示参数需要符合特质X1和X2同时也要有定义close方法
复合类型
class A extends B with C with D with E
应该理解为 class A extends (B with C with D with E) 这叫做复合类型
也可以通过Type声明别名
scala> type X = X1 with X2
defined type alias X
scala> def test(x:X) = println("OK")
test: (x: X)Unit
scala> class A extends X1 with X2
scala> val a = new A
scala> test(a)
OK
Type 定义类型
type S = String
可以给 String类型起一个别名 S
可以用于抽象类型
scala> trait A { type T ; def foo(i:T) = print(i) }
scala> class B extends A { type T = Int }
scala> val b = new B
scala> b.foo(200)
200
scala> class C extends A { type T = String }
scala> val c = new C
scala> c.foo("hello")
hello
交集类型和联合类型
X with Y with Z 开发交集类型
X or Y or Z 是并集类型,但是scala目前并不支持,可以使用隐式转换来实现
class StringOrInt[T]
object StringOrInt {
implicit object IntWitness extends StringOrInt[Int]
implicit object StringWitness extends StringOrInt[String]
}
object Bar {
def foo[T: StringOrInt](x: T) = x match {
case _: String => println("str")
case _: Int => println("int")
}
}
泛型子类型 父类型
在Java泛型里表示某个类型是Test类型的父类型,使用super关键字:
<T super Test>
//或用通配符的形式:
<? super Test>
scala 中使用
[T >: Test]
//或用通配符:
[_ >: Test]
lower bound适用于把泛型对象当作数据的消费者的场景下:
def append[T >: String] (buf : ListBuffer[T]) = {
buf.append( "hi")
}