为什么每个 Java 开发者都需要了解 Scala

前面我们一起回顾了第九期 Scala & Java Meetup 中最受关注的话题 —— jdk 并发编程的终极解决方案:虚拟线程,探讨了这一新特性对包括 Scala 在内的响应式编程语言的影响。

本次 Meetup 的首位分享者 Chunsen,在加入 Tubi 成为 Scala 开发者之前,曾在 Java 开发领域深耕多年。他分享了从 Java 转向 Scala 的一些体验,以及“回头再写 Java 一定会想念 Scala 的那些点”,也欢迎观看直播回放。

欢迎关注比图科技公众号,了解 Scala 最新资讯及活动。也欢迎你在后台留言,申请加入 Scala 开发交流群!

话题回顾

Java 语言的发展是非常成功的,尤其是在 Web 后台领域。自 1995 年诞生以来,Java 凭借网络服务的兴起,成为了互联网行业最常用的编程语言之一。Java 最初设计为一种面向对象的编程语言,旨在简化跨平台开发,具有可移植性和安全性,比 C++ 更简单易用,因此 Java 的很多特性与 C++ 比较类似。Scala 是一门在 2003 年始源于研究项目,旨在为 JVM 平台提供一种更具表达力和灵活性的编程语言,融合了面向对象和函数式编程的特性。

如果用一句话来总结 Java 和 Scala 的差异,那就是 Scala 作为一个相对于 Java 历史包袱更小的编程语言,可以更多地尝试融合更多现代化的优秀编程思想,进一步简化编程过程;同时,Scala 最初的设计者也在观察到 Java 语言发展过程中相对落后 / 冗余的方面后,借鉴了函数式编程等其他编程语言的优点,形成了编程风格简洁的多范式编程语言。!

Java 开发者非常适合了解学习 Scala 的原因

从宏观上来说,由于 Java 与 Scala 在渊源上的联系,Java 开发者学习 Scala 具有明显的优势。比如 Scala 和 Java 都是运行在 JVM(Java 虚拟机)上的编程语言,两者之间可以互相调用(有时也会存在不容易兼容的情况),并利用包括跨平台和成熟的生态系统在内的 JVM 优势以及 JVM 升级带来的性能提升等。

Scala 和 Java 各自都有很多优秀的库,可以比较容易地互相调用,这意味着 Scala 和 Java 代码可以混合编译,并共存于同一个项目中。

另外,学习一门新的编程语言不一定是为工作所用,也可以给开发者带来新的编程思想,并有可能进一步改变其编程思维。

Scala 相比 Java 在某些特性上有着更出色的表现

不是所有场景下 Scala 都比 Java 更好,但它有一些特性是将来再写 Java 一定会特别想念的,比如那些使代码表达力更强、更安全、更高效的特性。

第一,代码表达力更强

Scala 的代码表达力通过它的类型推导、模式匹配、命名参数与默认值、集合处理、隐式参数或隐式转换来体现。

类型推断

在创建变量时,如果编译器能确定变量的类型,就不需要手动声明了;而这样的操作确实让代码更简洁了。

模式匹配

Scala 模式匹配是一种强大的特性,可以用于检查数据类型、数据结构和数值的匹配,并根据匹配的结果执行相应的代码块。如下图所示,通过模式匹配,我们可以非常简单明了地实现一个获取所有叶子节点值的方法。

下图展示了一些模式匹配的常见用法。模式匹配不仅可以匹配类型,还可以匹配正则表达式,提高正则表达式代码的可读性。同时我们还可以通过定义 unapply 和 unapplySeq 的方式自定义模式匹配的行为。

命名参数 & 默认值

通过下图所呈现的代码,我们可以看到 Scala 可以在一个函数内实现函数参数有默认值,也就是多个函数的重载,而在 Java 开发中实现同样的功能我们往往需要写多个函数。

集合处理

如图中代码所示,通过对比 Java 和 Scala 计算平均工资的方法差异,我们可以容易地发现 Scala 在 Lambda 函数的使用和集合函数的处理上都是更加精简好用的。

隐式参数 / 隐式转换

隐式参数是一种特殊的参数,允许我们在调用函数时不显式地传递参数,而是让编译器根据上下文自动查找并传入相应的参数值。这种功能可以帮助简化代码,提高灵活性,并且常用于依赖注入、类型类和其它模式中。下图展示了隐式参数的使用。

第二,更安全

空指针安全

Java 编程获取 User 列表中最长的名字,需要注意判断空,容易出错。而在 Scala 中,我们可以使用函数式编程方式,避免出现没有判空的错误。

类型安全

Java 编译器允许把 String 数组赋值给 Object 数组,但在运行时当我们尝试给这个 Object 数组插入一个非 String 对象时就会产生错误。这是 Java 编译器在类型安全上的一个问题。同时,Java 编译器不允许把一个 String 列表赋值给 Object 列表,但是从逻辑上来说,我们希望这个这个赋值是可以发生的。而 Scala 编译器则能够解决了这两个问题,它不允许把一个 Sting 数组赋值给一个 Object 数组,但是允许把一个 String 列表赋值给一个 Object 列表。

如下图,我们定义了特质 Animal,Dog 继承 Animal,Husk 继承 Dog。Scala 中 Dog 继承了 Animal的特质,可以当作 Animal 的类型,不能当作 Husky 的类型;但在 Java 中都是不可以的(并不符合常理)。

Scala 编译器通过协变(Covariance)和逆变(Contravariance)来实现上述例子中的的类型安全,这也是为什么 Scala 比 Java 类型系统强大的一个原因。

协变表示类型参数的子类型关系与泛型类型之间的子类型关系是一致的。简而言之,如果类型 A 是类型 B 的子类型,那么 F[A] 也是 F[B] 的子类型。其中,F 表示一个泛型类型。

逆变表示类型参数的子类型关系与泛型类型之间的子类型关系是相反的。简而言之,如果类型 A 是类型 B 的子类型,那么 F[B] 是 F[A] 的子类型。与协变相反,逆变使用逆变符号 - 来表示。

模式匹配检查

Scala 编译器会检查我们的模式匹配代码是否穷举完了所有的 case,当我们忽略了一些 case,编译器会认为代码可能存在逻辑漏洞。这一特性可以很好地帮助我们发现逻辑中的漏洞,让代码更可靠。

第三,更高效

尾递归

Java 中使用递归函数可能会造成栈溢出(某些 Java 编译器在某些情况下会进行尾递归优化);而在 Scala 中,尾递归(Tail Recursion)是指递归函数中的递归调用发生在函数体的最后一个位置。当函数满足尾递归的条件时,编译器可以对其进行尾递归优化。

尾递归优化是一种编译器优化技术,它可以将尾递归函数转化为迭代形式,从而避免在递归过程中产生大量的函数调用栈。这样可以减少内存消耗并提高性能,因为不再需要保存每次递归调用的上下文信息。

如下图,最后一个调用不是该递归函数,则会发生栈溢出,不会进行尾递归的优化。

当稍加修改,在函数中保留一个变量,尾递归优化就可以实现了。

异步编程

在 Scala 中,Future 是表示异步计算结果的一种抽象类型。它允许在一个独立的执行上下文中进行并发计算,并在计算完成后获取结果。Future 通常用于处理异步操作,例如从远程服务器获取数据、执行耗时的计算或 IO 操作等。

在 Scala 中,宏(Macros)是一种元编程机制,允许在编译时对代码进行操作和生成。通过宏,开发人员可以在编译时期以及语法树级别上扩展和改变代码,从而实现更高级的抽象、优化和代码生成。

宏在 Scala 中被广泛应用于各种框架和库中,用于生成重复代码、优化性能、实现领域特定语言(DSL)等方面。然而,宏也是一种强大而复杂的元编程工具,需要谨慎使用,以避免引入难以理解和维护的代码。需要注意的是,自 Scala 2.13 版本起,宏已经被标记为“实验性功能”,而且在 Scala 3 中已经不再推荐使用宏,而是推荐使用新的“引用透明宏”(inline meta)功能。—— 以上总结来自现场观众谷国伟

热门话题讨论

提问:Scala 在实际生产中的应用怎么样?

Chunsen:大家对 Scala 在国内的情况应该比较了解,一直处于不温不火的状态;在国外相对更好。但其实,Scala 依然有很多忠实爱好者,包括我自己。

虽然随着 Java 的发展,语言本身存在缺陷的部分也在逐步弥补。Scala 刚发行时,Java 版本是 1.5,Java 确实存在很多问题影响编程体验,比如泛型尚不成熟、也没有 Lamda,那时 Scala 相对更有优势;而如今 Java 有 Recall /Lamda,也可以支持 Pental Match,这会导致大家对于 Scala 的需求没有那么强了,更何况还存在 Kotlin 这样一款更能抓住 Java 开发者心的、介于 Java 和 Scala 之间的一门编程语言。

但是,在我看来 Scala 依然具备一定的优势,尤其是在你对 Scala 有了更好的掌握和驾驭能力之后。在我使用 Scala 开发的体验中,Scala 确实使我们的代码更高效,业务逻辑更清晰了。

Guobin:如果你的团队相对于技术追求更看重业务本身,那么没有必要一定从 Java 转向 Scala。但是,我们个人学习一门技术或编程语言,并非为了在实际生产环境中使用,而是为了了解 Java 之外的编程思想。比如,作为 Java 开发者,你可能会认为 Lamda 就应该是这样的;但是,当你去学习了 Rust、Kotlin、Scala 等函数式编程语言后,你肯定会问自己“为什么 Java 的 Lamda 这样难用?” 在我看来,这是学习和使用另外一门编程语言所应该实现的效果。

如果你是为了在实际生产环境中使用一门新的编程语言,那么一定要考虑这一选择所带来的代价与收益。比如,当 Java 面对并发这一技术问题时,你可以考虑使用 Scala Akka 这一相对于 Java 非常不同的处理方式。在我看来,最关键的还是要找到“硬需求是什么”。我使用 Scala 的时间比 Java 更长,我有一些个人体验可以分享给大家。面向过程的 Java 开发很容易让人陷入细节的思考中,比如我们通过 Java 实现的算子、折叠方法可能不如标准部件实现得更好。而在 Scala 开发中,我们可以通过充分使用标准部件,避免投入过多的时间在细节的实现上,而将关注点放在更重要更根本的业务实现上。

Qiwen:Tubi 是我第一份写 Scala 的工作,之前我是 Java 开发者。在我看来,了解一门新的语言是为了学习它的写法和思想,而非是为了完全将一种服务从一门语言迁移到另一门语言上。我在 Amazon 工作时做的是Java 开发,虽然当时我们使用的版本是 1.8,但依然保持了一些更老的 Java 习惯。在我开始熟练使用 1.8 的 Lamda 时,会发现很多流程就是一个链,会带来更加清晰简洁的流程描述。当我来到 Tubi 开始写 Scala 时,我发现这竟然是 Scala 天生带有的一种能力,这也给了我机会反观 Java 开发经历。当我了解了 Scala 语言设计的初衷和思想,再回头理解 Java 相对应的一些实现时,我会更理解 Java 这门语言的一些特性,也更会在写代码时遵循那些最佳实践。

Chunsen:我很赞同 Guobin 提到的“在没有硬需求的情况下,没有必要硬转一门新的编程语言”;但我也想补充一点,了解和使用 Scala 一段时间究竟会给开发者带来什么?虽然我们都知道没有什么是 Scala 能实现而 Java 实现不了的,只不过在代码行数和美观性上存在差异,但是使用 Scala 的过程确实改变了我们思考问题的方式。Java 是面向过程的语言,面对一个问题的解决思路是一步一步地找到解决方案,如我在分享中提到的这个例子一样,当你需要在列表中找到用户名最长的用户,Java 的解决思路是使用 loop 循环并在中间加入定义,一步步找到。Scala 的解决思路是首先需要将用户名拿出来,根据用户名的长度排序找到最大的。这是两种完全不同的思维方式。在写了一段时间 Scala 后,我在回顾 Java 代码时会发现有很多可以被优化的地方。对我个人而言,这是我转写 Scala 后的最大收获之一。

欢迎加入 Tubi

Tubi Data Team 目前正在寻找一位大数据平台开发 Lead,他 / 她将领导数据开发团队,创建高质量、可扩展的流数据管道,与所有用户建立联系;将在开放创新的环境中与机器学习团队、产品经理、DevOps 团队和数据科学家合作,推动用户增长;对系统架构设计全面负责,解决性能、可扩展性、可重用性和灵活性等问题;并倡导工程最佳实践,培养与保持团队内的工程师文化;负责技术招聘和指导团队成员的职业发展,建立一支高效的开发团队。

首选编码语言为 Scala、Python 或 Java。

Scala Meetup 往期推荐

Tubi 秉承开放和回馈的心态,自 2019 年 8 月开始赞助并举办 Scala Meetup,如今已成功完成九期线上线下活动。

从 Scala 编程入门学习到职业发展,Scala 函数式编程语言的研究,还有 Scala 的生产实践,你所关心的 Scala 话题在往期 Meetup 中都有所涉及,这些共创交流也正在以某种方式回馈着社区中的每个人,让我们反复思考和精进“为什么选择 Scala?如何可以更好地发挥 Scala 的优势?Scala 怎样可以发展得更好?”

欢迎阅读往期回顾!

【活动回顾】2023年1月

【活动回顾】2022年6月

【活动回顾】2021年1月

【活动回顾】2020年9月

【活动回顾】2020年4月

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/258245.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringAMQP 快速入门

SpringAMQP 快速入门 1. 创建项目2. 快速入门2.2.1 消息发送2.2.2 消息接收 3. 交换机3.1 Fanout Exchange(扇出交换机)3.1.1 创建队列与交换机3.1.2 消息接收3.1.3 消息发送 3.2 Direct Exchange(直连交换机)3.2.1 创建交换机与队…

Java 简易版 TCP(一对一)聊天

客户端 import java.io.*; import java.net.Socket; import java.util.Date; import javax.swing.*;public class MyClient {private JFrame jf;private JButton jBsend;private JTextArea jTAcontent;private JTextField jText;private JLabel JLcontent;private Date data;p…

智能优化算法应用:基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用:基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用:基于跳蛛算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.跳蛛算法4.实验参数设定5.算法结果6.参考文献7.MATLAB…

springboot + thymeleaf + layui 初尝试

一、背景 公司运营的同事有个任务,提供一个数据文件给我,然后从数据库中找出对应的加密串再导出来给他。这个活不算是很难,但时不时就会有需求。 同事给我的文件有时是给excel表格,每一行有4列,逗号隔开,…

type property can‘t be changed 报错问题解决

问题 在使用 jQuery的 attr 方法对 input 输入框的 type 类型进行修改的时候报 type property can’t be changed 这个错误。 $psd.attr(type,text)原因 jQuery 的版本问题,当前使用的 jQuery 版本不允许修改 input 的 type属性所以报错。 解决方法 换原生 js …

2023_Spark_实验二十五:SparkStreaming读取Kafka数据源:使用Direct方式

SparkStreaming读取Kafka数据源:使用Direct方式 一、前提工作 安装了zookeeper 安装了Kafka 实验环境:kafka zookeeper spark 实验流程 二、实验内容 实验要求:实现的从kafka读取实现wordcount程序 启动zookeeper zk.sh start# zk.sh…

【面试经典150 | 二叉树】对称二叉树

文章目录 写在前面Tag题目来源解题思路方法一:递归方法二:迭代 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更…… 专栏内容以分析题目为主,并附带一些对于本题涉及到的…

云计算在数字营销中的作用是什么?

营销策略和云计算是一个为企业提供多种优势的系统。它使他们能够取得更大的成功,同时提高产量。这样做的原因是,可以从任何位置远程使用云集成工具和应用程序。基本上,该系统增强了存储设备和传播。同时,它减轻了公司 IT 网络的压…

上网行为审计软件丨紧盯小毛病,堵住大漏洞,守好钱袋子

上网行为审计软件是一种专门用于监控和分析员工或学生在计算机网络上的行为的软件。它可以帮助企业和家庭了解员工或学生的网络使用情况,发现和防止潜在的安全风险,提高工作效率和保护企业信息安全。 域之盾软件---上网行为审计系统的作用: …

排序-插入排序与希尔排序

文章目录 一、插入排序二、希尔排序 一、插入排序 思路: 当插入第i(i>1)个元素时,前面的array[0],array[1],…,array[i-1]已经排好序,此时用array[i]的排序码与array[i-1],array[i-2],…的排序码顺序进行比较,找到插入位置即将…

导入自定义模块出现红色波浪线,但是能正常执行

问题描述: 导入自己定义的模块时,出现红色波浪线,可以继续执行 解决: 在存放当前执行文件的文件夹右键,然后将其设置为sources root即可 结果:

什么是Active Directory 中的复制

AD 复制使更新的AD信息在所有 DC 中可用,从最简单的意义上讲,复制是在一个 DC 上修改 AD 对象的概念,然后将其复制并在 AD 林的所有其他 DC 上可见。 AD 环境有许多实时发生的不断变化,这些更改必须在 AD 数据库中更新&#xff0…