Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand the class syntax and semantics of Scala

2025-02-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/02 Report--

怎么理解Scala的类语法和语义,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

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

Scala 和 Java 一样使用类

我们不对 Scala 支持的类特性作冗长而抽象的讨论,而是着眼于一个类的定义,这个类可用于为 Scala 平台引入对有理数的支持:

清单 1. rational.scala

class Rational(n:Int, d:Int) { private def gcd(x:Int, y:Int): Int = { if (x==0) y else if (x

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

测试 Rational 类

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

清单 10. RationalTest.java

import org.junit.*; import static org.junit.Assert.*; public class RationalTest { @Test public void test2ArgRationalConstructor() { Rational r = new Rational(2, 5); assertTrue(r.numer() == 2); assertTrue(r.denom() == 5); } @Test public void test1ArgRationalConstructor() { Rational r = new Rational(5); assertTrue(r.numer() == 0); assertTrue(r.denom() == 1); // 1 because of gcd() invocation during construction; // 0-over-5 is the same as 0-over-1 } @Test public void testAddRationals() { Rational r1 = new Rational(2, 5); Rational r2 = new Rational(1, 3); Rational r3 = (Rational) reflectInvoke(r1, "$plus", r2); //r1.$plus(r2); assertTrue(r3.numer() == 11); assertTrue(r3.denom() == 15); } // ... some details omitted }

SUnit

现在已经有一个基于 Scala 的单元测试套件,其名称为 SUnit。如果将 SUnit 用于清单 10 中的测试,则不需要基于 Reflection 的方法。基于 Scala 的单元测试代码将针对 Scala 类进行编译,所以编译器可以构成符号行。一些开发人员发现,使用 Scala 编写用于测试 POJO 的单元测试实际上更加有趣。

SUnit 是标准 Scala 发行版的一部分,位于 scala.testing 包中(要了解更多关于 SUnit 的信息,请参阅 参考资料)。

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

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

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

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

"更好的" Java

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

清单 11. JavaPerson.java(原始 POJO)

public 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; }

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

清单 12. person.scala(线程安全的 POJO)

class Person(firstName:String, lastName:String, age:Int) { def getFirstName = firstName def getLastName = lastName def getAge = age override def toString = "[Person firstName:" + firstName + " lastName:" + lastName + " age:" + age + " ]" }

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

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

清单 13. person.scala(完整的 POJO)

class Person(var firstName:String, var lastName:String, var age:Int) { def getFirstName = firstName def getLastName = lastName def getAge = age def setFirstName(value:String):Unit = firstName = value def setLastName(value:String) = lastName = value def setAge(value:Int) = age = value override def toString = "[Person firstName:" + firstName + " lastName:" + lastName + " age:" + age + " ]" }

注意,构造函数参数引入了 var 关键字。简单来说, var 告诉编译器这个值是可变的。因此,Scala 同时生成 accessor( String firstName(void))和 mutator(void firstName_$eq(String))方法。然后,就可以方便地创建 setFoo 属性 mutator 方法,它在幕后使用生成的 mutator 方法。

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

看完上述内容,你们掌握怎么理解Scala的类语法和语义的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report