知识大全 面向Java开发人员的Scala指南: 实现继承
Posted 知
篇首语:空空的口袋不能阻碍你的未来,空空的脑袋你将永远贫穷。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 面向Java开发人员的Scala指南: 实现继承相关的知识,希望对你有一定的参考价值。
面向Java开发人员的Scala指南: 实现继承 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
Scala 对实现继承的支持与 Java&# ;语言一样丰富 — 但 Scala 的继承带来了一些惊喜 这个月 Ted Neward 介绍了以 Scala 方式完成的多态 还介绍了混合函数与面向对象的语言风格 同时使您依然能够完美地映射到 Java 平台的继承模型
近十几年来 面向对象语言设计的要素一直是继承的核心 不支持继承的语言(如 Visual Basic)被嘲讽是 玩具语言 不适合真正的工作 与此同时 支持继承的语言所采用的支持方法五花八门 导致了许多争论 多重继承是否真的必不可少(就像 C++ 的创作者认定的那样) 它是否不必要而丑陋的(就像 C# 和 Java 的创作者坚信的那样)?Ruby 和 Scala 是两种较新的语言 采取了多重继承的这种方法 — 正如我在上期介绍 Scala 的特征时所讨论的那样
与所有 杰出的语言一样 Scala 也支持实现继承 在 Java 语言中 单一实现继承模型允许您扩展基类 添加新方法和字段等 尽管存在某些句法变更 Scala 的实现继承依然类似于 Java 语言中的实现 不同的是 Scala 融合了对象和函数语言设计 这非常值得我们在本期文章中进行讨论
普通 Scala 对象
与本系列之前的文章类似 我将使用 Person 类作为起点 探索 Scala 的继承系统 清单 展示了 Person 的类定义
清单 嘿 我是人类
清单 嘿 我是人类
// This is Scalaclass Person(val firstName:String val lastName:String val age:Int) def toString = [Person: firstName= +firstName+ lastName= +lastName+ age= +age+ ] Person 是一个非常简单的 POSO(普通 Scala 对象 Plain Old Scala Object) 具有三个只读字段 您可能会想起 要使这些字段可以读写 只需将主构造函数声明中的 val 更改为 var 即可
无论如何 使用 Person 类型也非常简单 如清单 所示
清单 PersonApp
// This is Scalaobject PersonApp def main(args : Array[String]) : Unit = val bindi = new Person( Tabinda Khan ) System out println(bindi)
这算不上什么令人惊讶的代码 但给我们提供了一个起点
Scala 中的抽象方法
随着该系统的发展 越来越明显地意识到 Person 类缺乏一个成为 Person 的重要部分 这个部分是做些事情 的行为 许多人都会根据我们在生活中的作为来定义自己 而不是根据现有和占用的空间 因此 我会添加一个新方法 如清单 所示 这赋予了 Person 一些意义
清单 很好 做些事情!
// This is Scalaclass Person(val firstName:String val lastName:String val age:Int) override def toString = [Person: firstName= +firstName+ lastName= +lastName+ age= +age+ ] def doSomething = // uh what?
这带来了一个问题 Person 的用途究竟是什么?有些 Person 绘画 有些唱歌 有些编写代码 有些玩视频游戏 有些什么也不做(问问十几岁青少年的父母) 因此 我会为 Person 创建 子类 而不是尝试去将这些活动直接整合到 Person 本身之中 如清单 所示
清单 这个人做的事情很少
// This is Scalaclass Person(val firstName:String val lastName:String val age:Int) override def toString = [Person: firstName= +firstName+ lastName= +lastName+ age= +age+ ] def doSomething = // uh what?class Student(firstName:String lastName:String age:Int) extends Person(firstName lastName age) def doSomething = System out println( I m studying hard Ma I swear! (Pass the beer guys!) )
当尝试编译代码时 我发现无法编译 这是因为 Person doSomething 方法的定义无法工作 这个方法需要一个完整的主体(或许可抛出异常来表示它应在继承类中被覆蓋) 或者不需要主体 类似于 Java 代码中抽象方法的工作方式 我在清单 中尝试使用抽象的方法
清单 抽象类 Person
// This is Scalaabstract class Person(val firstName:String val lastName:String val age:Int) override def toString = [Person: firstName= +firstName+ lastName= +lastName+ age= +age+ ] def doSomething; // note the semicolon which is still optional // but stylistically I like having it hereclass Student(firstName:String lastName:String age:Int) extends Person(firstName lastName age) def doSomething = System out println( I m studying hard Ma I swear! (Pass the beer guys!) )
请注意 我如何使用 abstract 关键字装饰 Person 类 abstract 为编译器指出 是的 这个类应该是抽象的 在这方面 Scala 与 Java 语言没有区别
对象 遇到函数
由于 Scala 融合了对象和函数语言风格 我实际上建模了 Person(如上所述) 但并未创建子类型 这有些古怪 但强调了 Scala 对于这两种设计风格的整合 以及随之而来的有趣理念
回忆 前几期文章 Scala 将函数作为值处理 就像处理语言中的其他值一样 例如 Int Float 或 Double 在建模 Person 时 我可以利用这一点来获得 doSomething 不仅将其作为一种继承类中覆蓋的方法 还将其作为可调用 替换 扩展的 函数值 清单 展示了这种方法
清单 努力工作的人
// This is Scala class Person(val firstName:String val lastName:String val age:Int) var doSomething : (Person) => Unit = (p:Person) => System out println( I m + p + and I don t do anything yet! ); def work() = doSomething(this) override def toString = [Person: firstName= +firstName+ lastName= +lastName+ age= +age+ ] object App def main(args : Array[String]) = val bindi = new Person( Tabinda Khan ) System out println(bindi) bindi work() bindi doSomething = (p:Person) => System out println( I edit textbooks ) bindi work() bindi doSomething = (p:Person) => System out println( I write HTML books ) bindi work()
将函数作为第一建模工具是 Ruby Groovy 和 ECMAScript(也就是 JavaScript)等动态语言以及许多函数语言的常用技巧 尽管其他语言也可以用函数作为建模工具 (C++ 通过函数指针和/或成员函数指针实现 Java 代码中通过接口引用的匿名内部类实现) 但所需的工作比 Scala(以及 Ruby Groovy ECMAScript 和其他语言)多得多 这是函数语言使用的 高阶函数 概念的扩展
多亏 Scala 将函数视为值 这样您就可以在运行时需要切换功能的时候利用函数值 可将这种方法视为角色模式 —— Gang of Four 战略模式的一种变体 在这种模式中 对象角色(例如 Person 的当前就职状态)作为运行时值得到了更好的表现 比静态类型的层次结构更好
层次结构上层的构造函数
回忆一下编写 Java 代码的日子 有时继承类需要从构造函数传递参数至基类构造函数 从而使基类字段能够初始化 在 Scala 中 由于主构造函数出现在类声明中 不再是类的 传统 成员 因而将参数传递到基类将成为一个全新维度的问题
在 Scala 中 主构造函数的参数在 class 行传递 但您也可以为这些参数使用 val 修饰符 以便在类本身上轻松引入读值器(对于 var 则为写值器)
因此 清单 中的 Scala 类 Person 转变为清单 中的 Java 类 使用 javap 查看
清单 请翻译一下
// This is javapC:\\Projects\\scala inheritance\\code>javap classpath classes PersonCompiled from person scala public abstract class Person extends java lang Object implements scala ScalaObject public Person(java lang String java lang String int); public java lang String toString(); public abstract void doSomething(); public int age(); public java lang String lastName(); public java lang String firstName(); public int $tag();
JVM 的基本规则依然有效 Person 的继承类在构造时向基类传递某些内容 而不管语言强调的是什么 (实际上 这并非完全 正确 但在语言尝试规避此规则时 JVM 会表现失常 因此大多数语言仍然坚持通过某种方法为其提供支持 )当然 Scala 需要坚守此规则 因为它不仅需要保持 JVM 正常运作 而且还要保持 Java 基类正常运作 这也就是说 无论如何 Scala 必须实现一种语法 允许继承类调用基类 同时保留允许我们在基类上引入读值器和写值器的语法
为了将此放到更具体的上下文中 假设我通过以下方式编写了 清单 中的 Student 类
清单 坏学生!
// This is Scala// This WILL NOT pileclass Student(val firstName:String val lastName:String val age:Int) extends Person(firstName lastName age) def doSomething = System out println( I m studying hard Ma I swear! (Pass the beer guys!) )
本例中的编译器将运行很长一段时间 因为我尝试为 Student 类引入一组新方法(firstName lastName 和 age) 这些方法将与 Person 类上名称类似的方法彼此冲突 Scala 编译器不一定了解我是否正在尝试覆蓋基类方法(这很糟糕 因为我可以在这些基类方法后隐藏实现和字段) 或者引入相同名称的新方法(这也很糟糕 因为我可以在这些基类方法后隐藏实现和字段) 简而言之 您将看到如何成功覆蓋来自基类的方法 但那并不是我们目前要追求的目标
您还应注意到 在 Scala 中 Person 构造函数的参数不必一对一地与传递给 Student 的参数联系起来 这里的规则实际上与 Java 构造函数的规则完全相同 我们这样做只是为了便于阅读 同样 Student 可要求额外的构造函数参数 与在 Java 语言中一样 如清单 所示
清单 苛求的学生!
// This is Scalaclass Student(firstName:String lastName:String age:Int val subject:String) extends Person(firstName lastName age) def doSomething = System out println( I m studying hard Ma I swear! (Pass the beer guys!) )
您又一次看到了 Scala 代码与 Java 代码有多么的相似 至少涉及继承和类关系时是这样
语法差异
至此 您可能会对语法的细节感到迷惑 毕竟 Scala 并未像 Java 语言那样将字段与方法区分开来 这实际上是一项深思熟虑的设计决策 允许 Scala 程序员轻而易举地向使用基类的用户 隐藏 字段和方法之间的差异 考虑清单
清单 我是什么?
// This is Scalaabstract class Person(val firstName:String val lastName:String val age:Int) def doSomething def weight : Int override def toString = [Person: firstName= +firstName+ lastName= +lastName+ age= +age+ ] class Student(firstName:String lastName:String age:Int val subject:String) extends Person(firstName lastName age) def weight : Int = age // students are notoriously skinny def doSomething = System out println( I m studying hard Ma I swear! (Pass the beer guys!) ) class Employee(firstName:String lastName:String age:Int) extends Person(firstName lastName age) val weight : Int = age * // Employees are not skinny at all def doSomething = System out println( I m working hard hon I swear! (Pass the beer guys!) )
注意查看如何定义 weight 使其不带有任何参数并返回 Int 这是 无参数方法 因为它看上去与 Java 语言中的 专有 方法极其相似 Scala 实际上允许将 weight 定义为一种方法(如 Student 中所示) 也允许将其定义为字段/存取器(如 Employee 中所示) 这种句法决策使您在抽象类继承的实现方面有一定的灵活性 请注意 在 Java 中 即便是在同一个类中 只有通过 get/set 方法来访问各字段时 才能获得类似的灵活性 不知道判断正确与否 但我认为只有少数 Java 程序员会用这种方式编写代码 因此不经常使用灵活性 此外 Scala 的方法可像处理公共成员一样轻松地处理隐藏/私有成员
从 @Override 到 override
继承类经常需要更改在其某个基类内定义的方法的行为 在 Java 代码中 我们通过为继承类添加相同名称 相同签名的新方法来处理这个问题 这种方法的缺点在于签名录入的错误或含糊不清可能会导致没有征兆的故障 这也就意味着代码可以编译 但在运行时无法正确完成操作
为解决这个问题 Java 编译器引入了 @Override 注释 @Override 验证引入继承类的方法实际上已经覆蓋了基类方法 在 Scala 中 override 已经成为语言的一部分 几乎可以忘记它会生成编译器错误 因而 继承 toString() 方法应如清单 所示
清单 这是继承的结果
// This is Scalaclass Student(firstName:String lastName:String age:Int val subject:String) extends Person(firstName lastName age) def weight : Int = age // students are notoriously skinny def doSomething = System out println( I m studying hard Ma I swear! (Pass the beer guys!) ) override def toString = [Student: firstName= +firstName+ lastName= +lastName+ age= +age+ subject= +subject+ ]
非常简单明了
敲定
当然 允许继承覆蓋的反面就是采取措施防止它 基类需要禁止子类更改其基类行为 或禁止任何类型的继承类 在 Java 语言中 我们通过为方法应用修饰符 final 来实现这一点 确保它不会被覆蓋 此外 也可以为类整体应用 final 防止继承 实现层次结构在 Scala 中的效果是相同的 我们可以向方法应用 final 来防止子类覆蓋它 也可应用于类声明本身来防止继承
牢记 所有这些关于 abstract final 和 override 的讨论都同样适用于 名字很有趣的方法 (Java 或 C# 或 C++ 程序员可能会这样称呼运算符) 与应用于常规名称方法的效果相同 因此 我们常常会定义一个基类或特征 为数学函数设定某些预期(可以称之为 Mathable ) 这些函数定义抽象成员函数 + * 和 / 另外还有其他一些应该支持的数学运算 例如 pow 或 abs 随后 其他程序员可创建其他类型 — 可能是一个 Matrix 类 实现或扩展 Mathable 定义一些成员 看上去就像 Scala 以开箱即用的方式提供的内置算术类型
差别在于……
如果 Scala 能够如此轻松地映射到 Java 继承模型(就像本文至此您看到的那样) 就应该能够从 Java 语言继承 Scala 类 或反之 实际上 这必须 可行 因为 Scala 与其他编译为 Java 字节码的语言相似 必须生成继承自 java lang Object 的对象 请注意 Scala 类可能也要继承自其他内容 例如特征 因此实际继承的解析和代码生成的工作方式可能有所不同 但最终我们必须能够以某种形式继承 Java 基类 (切记 特征类似于有行为的接口 Scala 编译器将特征分成接口并将实现推入特征编译的目标类中 通过这种方式来使之运作 )
但结果表明 Scala 的类型层次结构与 Java 语言中的对应结构略有不同 从技术上来讲 所有 Scala 类继承的基类(包括 Int Float Double 和其他数字类型)都是 scala Any 类型 这定义了一组核心方法 可在 Scala 内的任意类型上使用 == != equals hashCode toString isInstanceOf 和 asInstanceOf 大多数方法通过名称即可轻松理解 在这里 Scala 划分为两大分支 原语类型 继承自 scala AnyVal 类类型 继承自 scala AnyRe (scala ScalaObject 又继承自 scala AnyRef )
通常 这并不是您要直接去操心的方面 但在考虑跨两种语言的继承时 可能会带来某些非常有趣的副作用 例如 考虑清单 中的 ScalaJavaPerson
清单 混合!
// This is Scalaclass ScalaJavaPerson(firstName:String lastName:String age:Int) extends JavaPerson(firstName lastName age) val weight : Int = age * // Who knows what Scala/Java people weigh? override def toString = [SJPerson: firstName= +firstName+ lastName= +lastName+ age= +age+ ]
……它继承自 JavaPerson
清单 看起来是否眼熟?
// This is Javapublic class JavaPerson public JavaPerson(String firstName String lastName int age) this firstName = firstName; this lastName = lastName; this age = age; public String getFirstName() return this firstName; public void setFirstName(String value) this firstName = value; public String getLastName() return this lastName; public void setLastName(String value) this lastName = value; public int getAge() return this age; public void setAge(int value) this age = value; public String toString() return [Person: firstName + firstName + lastName: + lastName + age: + age + ] ; private String firstName; private String lastName; private int age;
在编译 ScalaJavaPerson 时 它将照常扩展 JavaPerson 但按照 Scala 的要求 它还会实现 ScalaObject 接口 并照例支持继承自 JavaPerson 的方法 因为 ScalaJavaPerson 是一种 Scala 类型 我们可以期望它支持 Any 引用的指派 根据 Scala 的规则
清单 使用 ScalaJavaPerson
// This is Scala val richard = new ScalaJavaPerson( Richard Campbell ) System out println(richard) val host : Any = richard System out println(host)
但在 Scala 中创建 JavaPerson 并将其指派给 Any 引用时会发生什么?
清单 使用 JavaPerson
// This is Scala val carl = new JavaPerson( Carl Franklin ) System out println(carl) val host : Any = carl System out println(host )
结果显示 这段代码如期编译并运行 因为 Scala 能确保 JavaPerson 做正确的事情 这要归功于 Any 类型与 java lang Object 类型的相似性 实际上 几乎可以说 所有扩展 java lang Object 的内容都支持存储到 Any 引用之中 (存在一些极端情况 我听说过 但我自己还从未遇到过这样的极端情况 )
最终结果?出于实践的目的 我们可以跨 Java 语言和 Scala 混搭继承 而无需过分担心 (最大的麻烦将是试图了解如何覆蓋 名字很有趣的 Scala 方法 例如 ^=!# 或类似方法 )
结束语
在本月的文章中 我为您介绍了 Scala 代码和 Java 代码之间的高度相似性意味着 Java 开发人员可以轻松理解并使用 Scala 的继承模型 方法覆蓋的工作方式相同 成员可见性的工作方式相同 还有更多相同的地方 对于 Scala 中的所有功能 继承或许与 Java 开发中的对应部分最为相似 惟一需要技巧的部分就是 Scala 语法 这有着明显的差异
习惯两种语言中继承方法的相似之处和细微的差异 您就可以轻松编写您自己的 Java 程序的 Scala 实现 例如 考虑流行的 Java 基类和框架的 Scala 实现 如 JUnit Servlets Swing 或 SWT 实际上 Scala 团队已经提供了一个 Swing 应用程序 名为 OOPScala 它使用 JTable 通过相当少的几行代码(数量级远远低于传统 Java 的对应实现)提供了简单的电子表格功能
cha138/Article/program/Java/Javascript/201311/25481相关参考
知识大全 面向Java开发人员的Scala指南: Scala控制结构内部揭密
面向Java开发人员的Scala指南:Scala控制结构内部揭密 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起
知识大全 面向Java开发人员的Scala指南: 面向对象的函数编程
面向Java开发人员的Scala指南:面向对象的函数编程 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下
知识大全 面向Java开发人员的Scala指南: 增强Scitter库
面向Java开发人员的Scala指南:增强Scitter库 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一
知识大全 面向Java开发人员的Scala指南: 深入了解Scala并发性 了解 Scala 如何简化并发编
面向Java开发人员的Scala指南:深入了解Scala并发性了解Scala如何简化并发编 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后
知识大全 面向Java开发人员的Scala指南: 关于特征和行为
面向Java开发人员的Scala指南:关于特征和行为 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
知识大全 面向Java开发人员的Scala指南: 包和访问修饰符
面向Java开发人员的Scala指南:包和访问修饰符 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!
知识大全 面向Java开发人员的Scala指南: 构建计算器,第1 部分
面向Java开发人员的Scala指南:构建计算器,第1部分 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一
知识大全 面向Java开发人员的Scala指南: 构建计算器,第 2 部分
面向Java开发人员的Scala指南:构建计算器,第2部分 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一
知识大全 面向Java开发人员的Scala指南: 深入了解Scala并发性 了解 actor 如何提供新的应
面向Java开发人员的Scala指南:深入了解Scala并发性了解actor如何提供新的应 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后
知识大全 Java开发者的Scala指南: Scala+Twitter=Scitter
Java开发者的Scala指南:Scala+Twitter=Scitter 以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我