关于Scala的路徑依赖类型(Path-dependent type)
2011-11-25 18:44
519 查看
咋看这个术语,有点吓倒。其实不是什么新东西,也是讲关于内部类和外部类的事,不过两者有点区别。在Scala中,内部类和外部类的一些行为特性和Java差不多。 请看如下的Scala代码:
内部类Inner可以存取外部类成员(包括private成员),但外部类不能存取内部类的private成员. 内部类之所以能存取外部类的成员,是因为内部类隐含地持有外部类的实例。这点和Java是一样的,上述的代码可以用Java语言来表达:
注意Java和scala在创建成员内部类时的细微差别。
Scala的内部类比Java的内部类具有更多特性,因此Scala引入了路径依赖类型(Path-dependent type)的概念。像outer.Inner这样的类型就称为路径依赖类型。所谓路径,指的就是参考外部类所建立的实例的名称。 就outer.Inner这个类型来说,路径为outer。更重要的是,不同路径代表不同的类型。比如:
上述的i1,i2分别引用了不同类型的实例,其中i1所引用的实体类型是o1.Inner, i2引用的实体类型是o2.Inner。假如你尝试把o1.Inner的实例赋值给声明为o2.Inner类型的变量,编译器会报错:
注意:
路径依赖的是路径的名称,与路径所引用的实例无关,请看例子:
事实上,
o1.Inner和o2.Inner都是Outer#Inner的子类型, 请看例子
路径依赖类型可以被继承,请看例子
注意:Outer#Inner是不能被继承的
此外,路径依赖类型所在的路径必须是不可变的值。例如
这也很好理解,如果o1是var,那么他的类型将是不确定的,从而导致其路径依赖类型也不确定。
一般地,x0.x1.x2...xn.T是路径一类类型,只要满足:
a) x0 是不可变值(即x0是val所声明的变量)
b) x1, x2, ..., xn 都是不可变的属性
c) T 是xn内部类
下面看一个负责的例子:
对于Cat类而言,F是其类型成员之一, 但这里
cat1.F和cat2.F代表相同的类型,也就是说你可以把一个cat2.F类型的实例传递给一个声明为cat1.F类型的变量. 实际上,上述代码的最后四行等价于以下的代码:
当然new Cat#F也是合法的
说了这么多,你们觉得这种特性有什么用途?如果你知道,请评论一下。
class Outer { private val x = 10 class Inner { private val y = x + 10 } } val outer = new Outer val inner = new outer.Inner // inner变量可以显式声明类型 val inner: outer.Inner = new outer.Inner
内部类Inner可以存取外部类成员(包括private成员),但外部类不能存取内部类的private成员. 内部类之所以能存取外部类的成员,是因为内部类隐含地持有外部类的实例。这点和Java是一样的,上述的代码可以用Java语言来表达:
public class Op { public void test1(){ Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); System.out.println(inner); } public static void main(String[] args) { Op op = new Op(); op.test1(); } } class Outer { private int x = 10; class Inner { private int y = x + 10; } }
注意Java和scala在创建成员内部类时的细微差别。
Scala的内部类比Java的内部类具有更多特性,因此Scala引入了路径依赖类型(Path-dependent type)的概念。像outer.Inner这样的类型就称为路径依赖类型。所谓路径,指的就是参考外部类所建立的实例的名称。 就outer.Inner这个类型来说,路径为outer。更重要的是,不同路径代表不同的类型。比如:
val o1 = new Outer val o2 = new Outer val i1 = new o1.Inner val i2 = new o2.Inner
上述的i1,i2分别引用了不同类型的实例,其中i1所引用的实体类型是o1.Inner, i2引用的实体类型是o2.Inner。假如你尝试把o1.Inner的实例赋值给声明为o2.Inner类型的变量,编译器会报错:
val o1 = new Outer val o2 = new Outer val i: o2.Inner = new o1.Inner // 编译错误,类型不匹配
注意:
路径依赖的是路径的名称,与路径所引用的实例无关,请看例子:
val o1 = new Outer val o2 = o1 val i1: o1.Inner = new o1.Inner val i2: o2.Inner = new o1.Inner // 编译错误。尽管o2和o1引用同一个实例,但o1.Inner和o2.Inner是不同的,Scala编译器只根据路径名进行区分
事实上,
o1.Inner和o2.Inner都是Outer#Inner的子类型, 请看例子
val o1 = new Outer val o2 = new Outer val i1: Outer#Inner = new o1.Inner val i2: Outer#Inner = new o2.Inner
路径依赖类型可以被继承,请看例子
val o = new Outer class Some extends o.Inner val oi: Outer#Inner = new Some
注意:Outer#Inner是不能被继承的
此外,路径依赖类型所在的路径必须是不可变的值。例如
var o1 = new Outer val i = new o1.Inner // 编译错误。因为o1是var
这也很好理解,如果o1是var,那么他的类型将是不确定的,从而导致其路径依赖类型也不确定。
一般地,x0.x1.x2...xn.T是路径一类类型,只要满足:
a) x0 是不可变值(即x0是val所声明的变量)
b) x1, x2, ..., xn 都是不可变的属性
c) T 是xn内部类
下面看一个负责的例子:
class Food class Fish extends Food { override def toString = "魚" } abstract class Animal { type F <: Food def eat(f: F) } class Cat extends Animal { type F = Fish def eat(fish: Fish) { println("吃" + fish) } } val cat1 = new Cat val cat2 = new Cat cat1.eat(new cat1.F) // 吃魚 cat2.eat(new cat2.F) // 吃魚
对于Cat类而言,F是其类型成员之一, 但这里
cat1.F和cat2.F代表相同的类型,也就是说你可以把一个cat2.F类型的实例传递给一个声明为cat1.F类型的变量. 实际上,上述代码的最后四行等价于以下的代码:
type F1 = Fish type F2 = Fish cat1.eat(new F1) // 吃魚 cat1.eat(new F2) // 吃魚
当然new Cat#F也是合法的
说了这么多,你们觉得这种特性有什么用途?如果你知道,请评论一下。
相关文章推荐
- scala类型系统:4) 内部类,路径依赖类型&类型投影
- 依赖名称不是类型[关于模板类中的迭代器]
- [原创]Scala学习:关于变量(val,var,类型推断)
- Scala教程(十六)Scala复合类型与依赖注入详解
- 关于模板中的嵌套依赖类型名与typename
- 关于scala中的参数化类型(泛型)
- 依赖名称不是类型[关于模板类中的迭代器]
- 记录一下,关于错误提示:could not find a part of path “X:\”的解决办法
- 关于SAP的视图类型
- 关于loose.dtd和xhtml1-transitional.dtd等文档类型定义模型中CSS失效的解决办法。
- 关于Mybatis中遇到的“无效列类型:1111”
- 关于path_alloc函数(APUE)
- Scala类型系统——高级类类型(higher-kinded types)
- 关于java中使用的view层的依赖
- Scala中List的构造是的类型约束逆变、协变、下界详解
- Scala 类型参数的界定
- 关于C++ map容器的使用和pair类型及函数
- 关于Javac命令和类文件之间的依赖问题
- 关于字符和数字类型的索引,Oracle如何实现内部自动转换以及索引使用的验证测试
- 深入理解Scala中的类型系统