`
edge
  • 浏览: 69192 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Scala概述(六)合成(2)

阅读更多

 

成员(Membership

如前所示,Iter 类从StringIteratorRichIterator 同时继承了类成员(members )。简单而言,一个类从以混入合成方式继承Cn with … with C1 ,将会继承其中所有类的成员,同时还可以自定义新的成员。由于Scala 保留了JavaC# 的静态重载机制,因此可能从父类继承同名的方法,也可以再定义同名的方法[1] 。为了判断类C 的一个方法到底是覆盖父类中的同名方法,还是这两个方法并存——即重载的关系,Scala 采用了匹配(matching )法,这也是从JavaC# 中类似的概念衍生来的:简单地说,如果两个类成员同名,并且具有相同的参数类型(如果两个都是方法),就称之为相匹配。

一个类的成员总共两种类型:具体和抽象的,每种类型分别对应一个判定规则:(注:下面几段是说明Scala 语言判断类成员属性的内部机制,不关心这方面细节的可以忽略)

一个类C 的具体成员是指其或其父类的所有具体声明M ,除非在其某个父类(也就是在L(C) )中已有一个匹配的具体成员。

一个类C 的抽象成员是指其或其父类的所有抽象声明M ,除非在C 中已有一个匹配的具体成员,或者其某个父类(也就是在L(C) )中有一个匹配的抽象成员。

这些规则同样决定了一个类C 与其父类之间匹配成员的覆盖关系。首先,具体成员一定覆盖抽象成员。其次,如果MM’ 同为具体成员或抽象成员,且MC 的全序化当中出现在M’ 之前,则M 覆盖M’

 

父类调用(Super Calls

我们考虑设计一个同步迭代器,也就是其操作在多线程之间是互斥的。

trait SyncIterator[T] extends AbsIterator[T] {

abstract override def hasNext: boolean =

synchronized(super .hasNext)

abstract override def next: T =

synchronized(super .next)

}

想要构造一个针对StringRich 、同步迭代器,可以用这三个类进行合成:

StringIterator(someString) with RichIterator[char]

with SyncIterator[char]

这个合成类从SynchIterator 继承了hasNextNext ,这两个方法都是对其父类的相应方法调用加了一个sychronized() 包装。

由于RichIteratorSyncIterator 定义的方法相互不重合(注:原文是RichIteratorStringIterator ,应该有误),因此它们出现在mixin 中的顺序没有影响,即上例写成这样也是等价的:

StringIterator(someString) with SyncIterator[char]

with RichIterator[char]

 

但是,这里有一个小细节要注意:在SyncIterator 中的super 这个调用并不是静态地绑定到其父类AbsIterator 上,因为显然这是毫无意义的,AbsIterator 定义的nexthasNext 都是抽象方法。实际上,这个super 调用实际上指向这个mixin 合成中的superclassStringIterator 的相应方法。从这个意义上讲,一个mixin 合成的superclass 覆盖了其各个mixin 当中静态声明的超类。这也就意味着super 调用在一个类当中无法被静态解析,必须延迟到一个类被实例化或被继承的时候才能解析出来。这一概念有如下精确定义:

假设CD 的父类,在C 当中的表达式super .M 应该能够静态解析为C 的某个父类当中的成员M ,这样才能保证类型正确。而在D 的语境中(context ,我将其翻译为语境,而不是通常人们翻译的“上下文”。这个问题说来话长,有机会的话会变成一篇文章甚至一本书——译者在这里顺便贩卖一下私货),这个表达式应该表示一个与M 相匹配的M’ ,这个成员应该在D 的全序当中位于C 之后的某个类里定义。

最后注意一点:在JavaC# 等语言中,上述SyncIterator 当中的这种super 调用明显是不合法的,因为它会被指派为父类当中的抽象成员(方法)。如同我们在上面看到的,这种构造在scala 中是合法的,只要保证一个前提,那就是这个类所出现的语境当中,其super 调用所访问的父类成员必须是具体定义了的。这一点是由SyncIterator 当中的abstractoverride 这两个关键字保证的。在scala 中,abstract override 这两个关键字成对出现在方法定义中,表明这个方法并没有获得完全的定义,因为它覆盖(并使用)了其父类当中的抽象成员。一个类如果有非完整定义的成员,它自身必须是抽象类,其子类必须将这些非完整定义的成员重新定义,才能进行实例化。

super 的调用可以是级联的,因此要遵从类的全序化(这是 Scala 的混入合成方式与多重继承方式之间最主要的差异)。例如,考虑另一个与 SyncIterator 类似的类,它将其返回的每个元素都打印到标准输出上:

trait LoggedIterator[T] extends AbsIterator[T] {

abstract override def next: T = {

val x = super .next; System.out.println(x); x

}

}

我们可以将这两种迭代子( sychronized logged )通过 mixin 组合在一起:

class Iter2 extends StringIterator(someString)

with SyncIterator[char]

with LoggedIterator[char]

在这里, Iter2 的全序化是:

{ Iter2, LoggedIterator, SyncIterator,

StringIterator, AbsIterator, AnyRef, Any }

这样一来, Iter2 next 方法继承自 LoggedIterator ,而该方法中的 super.next 则指向 SyncIterator next 方法,而后者当中的 super.next 则最终引用 StringIterator next 方法。

如果想对记录日志的动作进行同步,仅需要把两个 mixin 的顺序反过来即可实现:

class Iter2 extends StringIterator(someString)

with LoggedIterator[char]

with SyncIterator[char]

无论哪种情况, Iter2 next 方法当中 super 的调用都遵循其全序当中的父类顺序。



[1] 有人可能反对这种设计方式,认为这样太复杂,但是为了保证互操作性,这样做是必须的,例如一个 Scala 类继承一个 Java Swing 类的时候。

分享到:
评论
4 楼 bneliao 2009-08-06  

不知道martin引入self type和super的初衷是为了解决什么问题?

感觉用了self type和super以后,this和super的使用很不确定;不是很直观,有些难以理解
3 楼 edge 2009-08-03  
dcaoyuan 写道
引用
全序的引入固然使之在语法上的精确性得到保证,但对于编程者而言,在某些时刻如果不计算出全序的结果,甚至不知道到底继承了哪个方法。

好在Scala对方法本身的静态类型定义和检查,在IDE中,你可以Mouse over或Click调用的方法名,马上得知哪个方法被引用。

引用
反之,你写下一个可以被继承的方法时,甚至可能要考虑到如何被人继承才有更好效果/更不容易出现问题。

对Trait的使用,目前还需要更多的实际应用来判断适合的情景。以我观察Martin本人在Scala的API编程中,trait首先被当作Java中的interface使用,其次,在compiler库中,大部分情况下,trait中很少包含方法,而是被用做mixin class/object的容器,就是说,包含的是子类和Object。


嗯,没错,其实这个问题我表达的不太精确,应当主要是针对trait的。我这两句话一起说,就是针对文章当中这个例子。可以看到在trait当中如果还要包含super.XXX这类逻辑,在trait本身的design time是无法知道super的具体引用的,反之他被如何继承也无法知晓。因此是否应该在trait当中包含这样的逻辑,或者说具体应该包含什么逻辑,是存在着最佳实践的概念的,也就是还需要实践和摸索。

dcaoyuan 写道
当然,我们首先强调,复杂的手段应该主要由API的设计者使用。


这就是我后面的担忧中说的问题了,语言的设计者固然可以有这样的初衷或者(头脑中隐含的)限制,但是对于使用者而言,要他们时刻想着“应该在构建组件时使用一些复杂手段,解决普通编程需要时使用一些内建的便捷特性”,很容易造成一定程度的困惑,最终会导致这种语言的使用者被人为地(其实是自然地)划分为不同的群体,无法构成一个非常良好的社区。在我看来C++的现状就是这样形成的,对于语言的设计者而言有种悲哀的味道。
2 楼 dcaoyuan 2009-08-03  
引用
全序的引入固然使之在语法上的精确性得到保证,但对于编程者而言,在某些时刻如果不计算出全序的结果,甚至不知道到底继承了哪个方法。

好在Scala对方法本身的静态类型定义和检查,在IDE中,你可以Mouse over或Click调用的方法名,马上得知哪个方法被引用。

引用
反之,你写下一个可以被继承的方法时,甚至可能要考虑到如何被人继承才有更好效果/更不容易出现问题。

对Trait的使用,目前还需要更多的实际应用来判断适合的情景。以我观察Martin本人在Scala的API编程中,trait首先被当作Java中的interface使用,其次,在compiler库中,大部分情况下,trait中很少包含方法,而是被用做mixin class/object的容器,就是说,包含的是子类和Object。

当然,我们首先强调,复杂的手段应该主要由API的设计者使用。
1 楼 edge 2009-08-02  
Scala在对象体系方面的语法,到这里基本上完全展开了,大家可以看到其复杂性的特点以及其发明者这样设计的一些初衷。迄今为止的内容可以看到,scala的很多语法特性确实符合其创造者的预期,那就是它非常适合于编写精心设计的模块/组件,然后让别人去使用或者扩展。这一点上次和草原也深入讨论过,他也在考虑用scala写一些DSL性质的东西,与我的观点算是殊途同归吧。

我感觉这样的一个语法体系,其利弊尚难准确评判。举个例子而言:Scala中的继承这一概念,完全超出了直观的范畴,全序的引入固然使之在语法上的精确性得到保证,但对于编程者而言,在某些时刻如果不计算出全序的结果,甚至不知道到底继承了哪个方法。反之,你写下一个可以被继承的方法时,甚至可能要考虑到如何被人继承才有更好效果/更不容易出现问题。

不仅如此,scala的语法在不同的角度/维度体现出的特性,也存在着隐忧。比如说其scriptable的性质,也是很为martin所“炫耀”,但与前述这些对象体系并没有什么特别必然的内在联系。这不由得让我想起了BS所宣扬的C++的宗旨:multi paradigm programming,而C++的现状,也皆源于此。这样看起来,scala快要变成VM模型下的C++了,其前景也因此变得难于预测。

相关推荐

    Scala 概述(瑞士洛桑联邦理工)

    Scala 概述(瑞士洛桑联邦理工) Scala 概述(瑞士洛桑联邦理工)

    scala3 scala3 scala3 scala3 scala3

    Scala3的发布标志着该语言的进一步成熟,它引入了一系列改进,旨在解决早期版本中的一些痛点,同时保持对现有Scala2代码库的兼容性。 在Scala3中,最重要的变化之一是类型推断的增强。新的Typelevel Scala项目引入...

    scala sdk scala-2.12.3

    2. **标准库**:Scala的标准库提供了大量的类和模块,包括集合操作、I/O、反射、并发处理等,这些是编写Scala程序的基础。 3. **Scala REPL**:Read-Eval-Print Loop,交互式解释器,允许开发者即时测试代码片段,...

    scala netbeans 插件(2)

    scala netbeans 插件(2)scala netbeans 插件(2)scala netbeans 插件(2)

    Programming in Scala 2nd Edition

    2. **面向对象编程**:探讨Scala如何支持面向对象编程,包括类、对象、继承、多态等概念。 3. **函数式编程**:介绍函数式编程的核心理念,如纯函数、高阶函数、递归等,并展示了如何在Scala中实现这些概念。 4. **...

    m2e-scala.zip

    Eclipse Scala环境的配置 https://yanxml.blog.csdn.net/article/details/89250222 配套的下载资源. http://alchim31.free.fr/m2e-scala/update-site/ 这个地址被墙了.上传,方便大家离线安装`m2e-scala`.

    scala2.12.1Windows镜像包

    2. **Scala 2.12.1更新与改进**: - **性能提升**:相比于之前的版本,2.12.1在编译速度和运行效率上进行了优化。 - **JVM兼容性**:Scala 2.12.x主要针对Java 8及更高版本进行优化,充分利用了JVM的新特性,如...

    Scala函数式编程

    2 scala很多库在设计的时候,不理解原因,包括Option,Collection的很多看似有冗余的地方 3 很多scala的默认写法,不理解 4 多态的具体化,尤其是协变的意义所在 5 各种重载的符号使用 之前读过 programming in...

    scala-2.12.10.zip

    2. 将解压后的Scala目录添加到系统的PATH环境变量中,以便在命令行中直接调用 Scala 命令。 3. 确保已安装Java Development Kit (JDK) 8 或更高版本,因为Scala需要JVM才能运行。 4. 验证安装是否成功,可以在命令行...

    Scala-part2集合框架

    #### 集合概述 在Scala中,集合框架提供了丰富的数据结构,以便开发者能够高效地处理各种数据组织需求。集合主要分为三类:序列`Seq`、集`Set`以及映射`Map`。所有这些集合都扩展自`Iterable`特质,这意味着它们...

    大数据课程-Scala编程基础-1.Scala语言初识_lk_edit.ppt

    1. Scala概述:了解Scala的历史、目标和设计原则。 2. Scala的下载安装:确保安装了JDK 1.8或更高版本,然后下载Scala安装文件并配置环境变量。 3. IDEA中安装Scala插件:在IntelliJ IDEA中搜索并安装Scala插件,...

    scala + mybatis 数据库查询

    2. **Scala版的MyBatis配置** - 在Scala中,我们需要创建一个`SqlSessionFactoryBuilder`,然后使用它来构建`SqlSessionFactory`。这通常在应用的初始化阶段完成。 - 配置文件(如`mybatis-config.xml`)通常包含...

    scala实战高清讲解

    2. **类型系统** - 隐式转换:Scala允许隐式参数和隐式转换,有助于代码简洁性,但需谨慎使用以避免混淆。 - 类型推断:Scala具有强大的类型推断能力,允许编写更少的类型声明,提高代码可读性。 - 泛型:泛型...

    Scala考试题1

    2. **trait(特质)和 abstract class(抽象类)的区别**: - 特质(Trait)支持多重继承,可以看作是接口的扩展,可以包含具体实现。 - 抽象类可以有成员变量和部分方法的实现,但不能实例化。它支持单继承。 3....

    scala课件.zip

    Scala概述.pptx 变量.pptx 运算符.pptx 程序流程控制.pptx 函数式编程基础.pptx 面向对象编程(基础部分).pptx 面向对象编程(中级部分).pptx 面向对象编程(高级特性).pptx 隐式转换和隐式参数.pptx 集合(上)-基本使用...

    Scala语法简明教程

    for (i <- 1 to 100 if i % 2 == 0) println(i) ``` - **方法定义**: - 方法定义在Scala中更为简洁,支持默认参数、可变参数、命名参数等多种形式。 ```scala def f(x: Int): Int = x * x def a(x: Int, y: ...

    IntelliJ IDEA 集成Scala插件-2017.2.13.rar

    "IntelliJ IDEA 集成Scala插件-2017.2.13.rar"这个资源提供了在IntelliJ IDEA 2017.2版本中支持Scala开发的插件,这使得开发者可以在一个统一的环境中编写、调试和测试Scala代码,极大地提高了开发效率。Scala是一种...

    最好的scala学习 课件

    Scala是一种强大的多范式编程语言,它融合了面向对象和函数式编程的特性,被广泛应用于大数据处理领域,特别是与Apache Spark相结合时。本课件是针对Scala学习者精心准备的资源,旨在帮助你深入理解和掌握Scala的...

    scala习题精选100道附带解析

    - **知识点概述**:Scala提供了多种数值类型以适应不同的数据处理需求。这些类型包括整数类型、浮点数类型以及字符类型等。 - **详细解析**: - **Char**:16位无符号Unicode字符。 - **Int**:32位有符号整数。 ...

Global site tag (gtag.js) - Google Analytics