初学者求助一段scala调Java代码代码,快被它的语法搞疯

第一天中只是些简单应用 ,您只是稍微了解了一些 Scala 语法,这些是运行 Scala 程序和了解其简单特性的最基本要求。通过上一篇文章中的 Hello World 和 Timer 示例程序,您了解了 Scala 的 Application 类、方法定义和匿名函数的语法,还稍微了解了 Array[] 和一些类型推断方面的知识。Scala 还提供了很多其他特性,本文将研究 Scala 编程中的一些较复杂方面。

Scala 的函数编程特性非常引人注目,但这并非 Java 开发人员应该对这门语言感兴趣的惟一原因。实际上,Scala 融合了函数概念和面向对象概念。为了让 Java 和 Scala 程序员感到得心应手,可以了解一下 Scala 的对象特性,看看它们是如何在语言方面与 Java 对应的。记住,其中的一些特性并不是直接对应,或者说,在某些情况下,“对应” 更像是一种类比,而不是直接的对应。不过,遇到重要区别时,我会指出来。

我们不对 Scala 支持的类特性作冗长而抽象的讨论,而是着眼于一个类的定义,这个类可用于为 Scala 平台引入对有理数的支持(主要借鉴自 “Scala By Example”,参见 参考资料):

从词汇上看,清单 1 的整体结构与 Java 代码类似,但是,这里显然还有一些新的元素。在详细讨论这个定义之前,先看一段使用这个新 Rational 类的代码:

清单 2 中的内容平淡无奇:先创建两个有理数,然后再创建两个 Rational,作为前面两个有理数的和与差,最后将这几个数回传到控制台上(注意, Console.println() 来自 Scala 核心库,位于 scala.* 中,它被隐式地导入每个 Scala 程序中,就像 Java 编程中的 java.lang 一样)。


现在,回顾一下 Rational 类定义中的第一行:


您也许会认为清单 3 中使用了某种类似于泛型的语法,这其实是 Rational 类的默认的、首选的构造函数:n 和 d 是构造函数的参数。

Scala 优先使用单个构造函数,这具有一定的意义 —— 大多数类只有一个构造函数,或者通过一个构造函数将一组构造函数 “链接” 起来。如果需要,可以在一个 Rational 上定义更多的构造函数,例如:


注意,Scala 的构造函数链通过调用首选构造函数(Int,Int 版本)实现 Java 构造函数链的功能。

在处理有理数时,采取一点数值技巧将会有所帮助:也就是说,找到公分母,使某些操作变得更容易。如果要将 1/2 与 2/4 相加,那么 Rational 类应该足够聪明,能够认识到 2/4 和 1/2 是相等的,并在将这两个数相加之前进行相应的转换。

嵌套的私有 gcd() 函数和 Rational 类中的 g 值可以实现这样的功能。在 Scala 中调用构造函数时,将对整个类进行计算,这意味着将 g 初始化为 n 和 d 的最大公分母,然后用它依次设置 n 和 d。

然而,请注意 toString 的语法:定义前面的 override 关键字是必需的,这样 Scala 才能确认基类中存在相应的定义。这有助于预防因意外的输入错误导致难于觉察的 bug(Java 5 中创建 @Override 注释的动机也在于此)。还应注意,这里没有指定返回类型 —— 从方法体的定义很容易看出 —— 返回值没有用 return 关键字显式地标注,而在 Java 中则必须这样做。相反,函数中的最后一个值将被隐式地当作返回值(但是,如果您更喜欢 Java 语法,也可以使用 return 关键字)。

在形式上,Scala 调用无参数的 numer 和 denom 方法,这种方法用于创建快捷的语法以定义 accessor。Rational 类仍然有 3 个私有字段:n、d 和 g,但是,其中的 n 和 d 被默认定义为私有访问,而 g 则被显式地定义为私有访问,它们对于外部都是隐藏的。

此时,Java 程序员可能会问:“n 和 d 各自的 ‘setter’ 在哪里?” Scala 中不存在这样的 setter。Scala 的一个强大之处就在于,它鼓励开发人员以默认方式创建不可改变的对象。但是,也可使用语法创建修改 Rational 内部结构的方法,但是这样做会破坏该类固有的线程安全性。因此,至少对于这个例子而言,我将保持 Rational 不变。

当然还有一个问题,如何操纵 Rational 呢?与 java.lang.String 一样,不能直接修改现有的 Rational 的值,所以惟一的办法是根据现有类的值创建一个新的 Rational,或者从头创建。这涉及到 4 个名称比较古怪的方法:+、 -、* 和 /。

与其外表相反,这并非操作符重载。

记住,在 Scala 中一切都是对象。在上一篇 文章 中, 您看到了函数本身也是对象这一原则的应用,这使 Scala 程序员可以将函数赋予变量,将函数作为对象参数传递等等。另一个同样重要的原则是,一切都是函数;也就是说,在此处,命名为 add 的函数与命名为 + 的函数没有区别。在 Scala 中,所有操作符都是类的函数。只不过它们的名称比较古怪罢了。

在 Rational 类中,为有理数定义了 4 种操作。它们是规范的数学操作:加、减、乘、除。每种操作以它的数学符号命名:+、-、 * 和 /。

但是请注意,这些操作符每次操作时都构造一个新的 Rational 对象。同样,这与 java.lang.String 非常相似,这是默认的实现,因为这样可以产生线程安全的代码(如果线程没有修改共享状态 —— 默认情况下,跨线程共享的对象的内部状态也属于共享状态 —— 则不会影响对那个状态的并发访问)。

一切都是函数,这一规则产生两个重要影响:

首先,您已经看到,函数可以作为对象进行操纵和存储。这使函数具有强大的可重用性,本系列 第一篇文章 对此作了探讨。

第二个影响是,Scala 语言设计者提供的操作符与 Scala 程序员认为应该 提供的操作符之间没有特别的差异。例如,假设提供一个 “求倒数” 操作符,这个操作符会将分子和分母调换,返回一个新的 Rational (即对于 Rational(2,5) 将返回 Rational(5,2))。如果您认为 ~ 符号最适合表示这个概念,那么可以使用此符号作为名称定义一个新方法,该方法将和 Java 代码中任何其他操作符一样,如清单 5 所示:

在 Scala 中定义这种一元 “操作符” 需要一点技巧,但这只是语法上的问题而已:

当然,需要注意的地方是,必须在名称 ~ 之前加上前缀 “unary_”,告诉 Scala 编译器它属于一元操作符。因此,该语法将颠覆大多数对象语言中常见的传统 reference-then-method 语法。

这条规则与 “一切都是对象” 规则结合起来,可以实现功能强大(但很简单)的代码:

Scala 编译器甚至会尝试推断具有某种预定含义的 “操作符” 的其他含义,例如 += 操作符。注意,虽然 Rational 类并没有显式地定义 +=,下面的代码仍然会正常运行:

记住,Scala 将被编译为 Java 字节码,这意味着它在 JVM 上运行。如果您需要证据,那么只需注意编译器生成以 0xCAFEBABE 开头的 .class 文件,就像 javac 一样。另外请注意,如果启动 JDK 自带的 Java 字节码反编译器(javap),并将它指向生成的 Rational 类,将会出现什么情况,如清单 9 所示:

Scala 类中定义的 “操作符” 被转换成传统 Java 编程中的方法调用,不过它们仍使用看上去有些古怪的名称。类中定义了两个构造函数:一个构造函数带有一个 int 参数,另一个带有两个 int 参数。您可能会注意到,大写的 Int 类型与 java.lang.Integer 有点相似,Scala 编译器非常聪明,会在类定义中将它们转换成常规的 Java 原语 int。

一种著名的观点认为,优秀的程序员编写代码,伟大的程序员编写测试;到目前为止,我还没有对我的 Scala 代码严格地实践这一规则,那么现在看看将这个 Rational 类放入一个传统的 JUnit 测试套件中会怎样,如清单 10 所示:

除了确认 Rational 类运行正常之外,上面的测试套件还证明可以从 Java 代码中调用 Scala 代码(尽管在操作符方面有点不匹配)。当然,令人高兴的是,您可以将 Java 类迁移至 Scala 类,同时不必更改支持这些类的测试,然后慢慢尝试 Scala。

您惟一可能觉得古怪的地方是操作符调用,在本例中就是 Rational 类中的 + 方法。回顾一下 javap 的输出,Scala 显然已经将 + 函数转换为 JVM 方法 $plus,但是 Java 语言规范并不允许标识符中出现 $ 字符(这正是它被用于嵌套和匿名嵌套类名称中的原因)。

为了调用那些方法,需要用 Groovy 或 JRuby(或者其他对 $ 字符没有限制的语言)编写测试,或者编写 Reflection 代码来调用它。我采用后一种方法,从 Scala 的角度看这不是那么有趣,但是如果您有兴趣的话,可以看看本文的代码中包含的结果(参见 下载)。

注意,只有当函数名称不是合法的 Java 标识符时才需要用这类方法。


我学习 C++ 的时候,Bjarne Stroustrup 建议,学习 C++ 的一种方法是将它看作 “更好的 C 语言”(参见 参考资料)。在某些方面,如今的 Java 开发人员也可以将 Scala 看作是 “更好的 Java”,因为它提供了一种编写传统 Java POJO 的更简洁的方式。考虑清单 11 中显示的传统 Person POJO:

现在考虑用 Scala 编写的对等物:

这不是一个完全匹配的替换,因为原始的 Person 包含一些可变的 setter。但是,由于原始的 Person 没有与这些可变 setter 相关的同步代码,所以 Scala 版本使用起来更安全。而且,如果目标是减少 Person 中的代码行数,那么可以删除整个 getFoo 属性方法,因为 Scala 将为每个构造函数参数生成 accessor 方法 —— firstName()

即使必须包含这些可变的 setter 方法,Scala 版本仍然更加简单,如清单 13 所示:

Scala 将函数概念与简洁性相融合,同时又未失去对象的丰富特性。从本系列中您可能已经看到,Scala 还修正了 Java 语言中的一些语法问题(后见之明)。

本文是面向 Java 开发人员的 Scala 指南 系列中的第二篇文章,本文主要讨论了 Scala 的对象特性,使您可以开始使用 Scala,而不必深入探究函数方面。应用目前学到的知识,您现在可以使用 Scala 减轻编程负担。而且,可以使用 Scala 生成其他编程环境(例如 Spring 或 Hibernate )所需的 POJO。

但是,请继续关注本系列,下次将开始讨论 Scala 的函数方面。


}

// scala语法中,类并不声明为public,一个Scala源文件可以包含多个类。所有这些类都具有公有可见性。所以这里的修饰符在后面扩展时再介绍

// java语法中要求一个java源码文件中可以声明多个类,但是公共类只能有一个,且必须和源码文件的文件名保持一致。

// scala在声明对象变量时,可以根据创建对象的类型自动推断,所以类型声明可以省略

但当类型和后面new 对象类型有继承关系即多态时,就必须写了

// Java是强类型语言,声明任何变量的同时都必须声明类型

官方不推荐程序员自己编写get和set方法,因为只要class中的成员变量不加private修饰符,scala编译器会自动把成员变量变为私有的同时,增加公有的get和set方法

// Java语法中如果声明了一个属性,但是没有显示的初始化,那么会给这个属性默认初始化

// setter/getter方法一般都是公共访问权限,起到了封装的作用

// Scala中的方法其实就是函数,只不过一般将对象中的函数称之为方法。声明规则请参考函数式编程中的函数声明

1.5.1函数既是方法也是值

在函数式编程语言中,函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

注意:方法名+(参数)为执行并返回结果, 方法名+ _ 则返回方法的引用

1.5.2参数与返回值

// 不写return,也不写返回值, ,程序会根据最后一行的结果作为return的返回值,类型自动推断。

// 写了 return 必须写返回值,但是写了返回值可以不写return,默认取最后一行。

// 如果连=号都不写,默认为Unit

注意:有return 无法省略返回值,没有return 可以自行推断

          调用函数时,如果不传递参数,就采用默认的初始值,如果传递参数,传递的参数会覆盖初始化。

          如果函数存在多个参数,每一个参数都可以设定默认值,那么这个时候,传递的参数到底是覆盖默认值,还是赋值给没有默认值的参数,就不确定了(默认按照声明顺序)。在这种情况下,可以采用带名参数

//就是没有副作用的函数,这里所谓的没有副作用,指的是函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。 函数与外界交换数据只有一个唯一渠道——参数和返回值

(1).易并行: 线程安全,不需要线程同步.

(2).易推断:因为所有的影响因素都在参数中,所以很容易理解函数的功能。

(3).易调试: 不用关心上下文,只关心所传入参数,是否能够计算出对应的值。

(4).易组合: 纯函数与纯函数组合一定还是纯函数

(5) 易缓存: 因为相同的参数一定返回相同的结果,所以可以利用参数缓存结果。

(6) 幂等: 不论该函数被执行多少次,都不会影响环境,而结果也一样。

(7)  可延迟处理: 结果不会因为时间而改变,只要传递的参数值确定,什么时候执行,结果都是一定的。

1.6伴生对象(类比为静态方法及成员的集合)

// Scala语言并没有静态的概念。但是为了能够和Java语言交互,就产生了一种特殊的对象来模拟类对象,我们称之为类的伴生对象。这个类的所有静态内容都可以放置在它的伴生对象中声明和调用

// Scala中伴生对象采用object关键字声明,伴生对象中声明的全是“静态”内容,可以通过伴生对象名称直接调用。

// 伴生对象对应的类称之为伴生类,伴生类和伴生对象应该在同一个源码文件中。

// 伴生对象中的属性和方法都可以通过类名直接调用访问。

// 从语法角度来讲,所谓的伴生对象其实就是类的静态方法和成员的集合

// scala 包名和源码实际的存储位置没有关系

// 从技术角度来讲,Scala的编译器会将Scala中的包编译成符合Java语法规则的包结构

//子类继承父类的属性和方法

1.9特质(类比接口)

// Scala语言中,采用特质(特征)来代替接口的概念,也就是说,多个类具有相同的特征(特征)时,就可以将这个特质(特征)独立出来,采用关键字trait声明

// 一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父类,那么需要采用with关键字连接

}

由于Scala是基于JVM的数据分析和处理,Scala比Python快10倍。当编写Python代码用且调用Spark库时,性能是平庸的,但如果程序涉及到比Python编码还要多的处理时,则要比Scala等效代码慢得多。Python解释器PyPy内置一个JIT(及时)编译器,它很快,但它不提供各种Python C扩展支持。在这样的情况下,对库的C扩展CPython解释器优于PyPy解释器。

使用Python在Spark的性能开销超过Scala,但其重要性取决于您在做什么。当内核数量较少时,Scala比Python快。随着核数的增加,Scala的性能优势开始缩小。

当大量的处理其工作时,性能不是选择编程语言的主要驱动因素。然而,当有重要的处理逻辑时,性能是一个主要因素,Scala绝对比Python提供更好的性能,用于针对Spark程序。

在用Scala语言编写Spark程序时有几个语法糖,所以大数据专业人员在学习Spark时需要非常小心。程序员可能会发现Scala语法有时会让人发疯。Scala中的一些库很难定义随机的符号运算符,而这些代码可以由没有经验的程序员理解。在使用Scala时,开发人员需要关注代码的可读性。与Scala相比,Java或Python是一个灵活的语法复杂的语言。对Scala开发人员的需求越来越大,因为大数据公司重视能在Spark中掌握数据分析和处理的高效而健壮的开发人员。

Python是为Java程序员学习相对容易的因为它的语法和标准库。然而,Python是不是一个高度并行和可扩展的像SoundCloud或推特系统的理想选择。

学习Scala丰富了程序员对类型系统中各种新抽象的认识,新的函数编程特性和不可变数据。

大数据系统的复杂多样的基础结构需要一种编程语言,它有能力集成多个数据库和服务。在大数据的生态系统中,Scala胜在Play框架提供了许多异步库和容易集成的各种并发原语,比如Akka。Scala使开发人员编写高效的、可读性和可维护性的服务而不是。相反,Python不支持的重量级进程并行在用uWSGI时,但它不支持真正的多线程。

当使用Python写Spark程序时,不管进程有多少线程,每次只有一个CPU在Python进程中处于活动状态。这有助于每个CPU核心只处理一个进程,但糟糕的是,每当部署新代码时,需要重新启动更多的进程,还需要额外的内存开销。Scala在这些方面更高效,更容易共事。

当用Spark编程时,开发人员需要根据变化的需求不断地重新编码代码。Scala是静态类型语言,尽管它看起来像一种动态类型语言,因为它具有优雅的类型推断机制。作为静态类型语言,Scala仍然提供编译器来捕获编译时错误。

重构像Scala这样的静态类型语言的程序代码比重构像Python这样的动态语言代码要容易得多且简单。开发人员在修改Python程序代码后常常会遇到困难,因为它造成的bug比修复程序原有的bug要多。所以最好是缓慢而安全地使用Scala,而不是快速的、死地使用Python。

对于小型的特殊实验,Python是一种有效的选择,但它并不像静态语言那样有效地扩展到大型软件工程中。

Scala和Python语言在Sparkcontext中有同样的表达,因此通过使用Scala或Python可以实现所需的功能。无论哪种方式,程序员都会创建一个Sparkcontext并调用函数。Python是一种比Scala更便于用户使用的语言。Python不那么冗长,开发人员很容易用Python编写脚本来调用Spark。易用性是一个主观因素,因为它取决于程序员的个人偏好。

Scala编程语言有几个存在类型、宏和隐式。Scala的晦涩难懂的语法可能很难对开发人员可能无法理解的高级特性进行实验。然而,Scala的优势在于在重要的框架和库中使用这些强大的特性。

话虽如此,Scala没有足够的数据科学工具和库,如Python用于机器学习和自然语言处理。Sparkmlib–机器学习库只有较少的ML算法但他们是理想的大数据处理。Scala缺乏良好的可视化和本地数据转换。Scala无疑是Spark streaming特性的最佳选择,因为Python 通过pySpark

“Scala速度更快,使用方便 但上手难,而Python则较慢,但很容易使用。”

Spark框架是用Scala编写的,所以了解Scala编程语言有助于大数据开发人员轻松地挖掘源代码,如果某些功能不能像预期的那样发挥作用。使用Python增加了更多问题和bug的可能性,因为2种不同语言之间的转换是困难的。为Spark使用Scala提供对Spark框架的最新特性的访问,因为它们首先在Scala中可用,然后移植到Python中。

根据Spark决定Scala和Python取决于最适合项目需要的特性,因为每种语言都有自己的优点和缺点。在使用Apache Spark编程语言之前,开发者必须学习Scala和Python来熟悉它们的特性。学习了Python和Scala之后,决定何时使用Scala来Spark以及何时使用Python来调用Spark是相当容易的。Apache Spark编程语言的选择完全取决于要解决的问题。

}

我要回帖

更多关于 scala调Java代码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信