scala语法_Java开发人员的Scala语法

这篇博客为Java开发者提供了Scala语法的简明指南,包括类型声明的位置、变量的不可变性(val)、可变性(var)、类型推断、方法和函数的声明、分号的使用、调用方法的括号、返回值处理、单位表示、匿名函数、字符串插值、泛型类型表示以及一些特别的语法特性如伴生对象和模式匹配。通过这些对比,Java开发者可以更好地理解和过渡到Scala编程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

scala语法

Java developers who are curious about Scala might find the differences in syntax to be daunting. So below is a guide to some of the common Scala syntax rules that differ from Java’s. At the end, we’ll walk through a comprehensive example of Scala code.

对Scala感到好奇的Java开发人员可能会发现语法差异令人生畏。 因此,以下是一些不同于Java的常见Scala语法规则的指南。 最后,我们将逐步介绍Scala代码的综合示例。

Note that we won’t explore the advanced, powerful features of Scala here. For-comprehensions, implicits, pattern-matching, and other features are better discussed in dedicated articles. Instead, this article will help any Java developers who are interested in exploring Scala, but are simply having a hard time understanding the code examples they’re seeing.

请注意,这里我们不会探讨Scala的高级功能。 对于理解隐式模式匹配和其他功能,可以在专门的文章中进行更好的讨论。 取而代之的是,本文将为所有对Scala感兴趣的Java开发人员提供帮助,但他们只是很难理解他们所看到的代码示例。

Let’s dive in.

让我们潜入。

类型声明位于变量名之后 (Type declarations come after variable names)

In Java, we declare types, then variable names, like so:Animal a = getAnimal();

在Java中,我们先声明类型,然后声明变量名,如下所示: Animal a = getAnimal();

In Scala, the type comes afterwards:val a: Animal = getAnimal();

在Scala中,类型随后出现: val a: Animal = getAnimal();

变量声明为不可变(val)或可变(var) (Variables are declared as either immutable (val) or mutable (var))

Rather than beginning a variable declaration with the class name, as we do in Java, we begin them with either val or var in Scala.

与其像在Java中那样用类名开始变量声明,不如在Scala中以valvar开头。

val (short for value) indicates that the variable is immutable (similar to final in Java):val a: Animal = getAnimal(); // `a` cannot be subsequently reassigned

val ( value的缩写)表示变量是不可变的(类似于Java中的final ): val a: Animal = getAnimal(); // `a` cannot be subsequently reassigned val a: Animal = getAnimal(); // `a` cannot be subsequently reassigned

var (short for variable) indicates that the variable is mutable:var a: Animal = getAnimal(); // `a` can be subsequently reassigned

var ( 变量的缩写)表示变量是可变的: var a: Animal = getAnimal(); // `a` can be subsequently reassigned var a: Animal = getAnimal(); // `a` can be subsequently reassigned

Scala favors immutability, so most often, you’ll see val used.

Scala赞成不变性,因此大多数情况下,您会看到使用了val

通常可以省略类型声明 (Type declarations can usually be omitted)

Scala has very strong type inference. So we rarely need to explicitly declare the type. That’s one reason why the class comes last when declaring a variable; it’s easy to simply omit it that way:var a = getAnimal(); // `a` is still an instance of Animal

Scala具有非常强的类型推断。 因此,我们很少需要显式声明类型。 这就是为什么在声明变量时类排在最后的原因。 可以很容易地以这种方式省略它: var a = getAnimal(); // `a` is still an instance of Animal var a = getAnimal(); // `a` is still an instance of Animal

不需要分号来表示行尾 (Semicolons are not required to denote the end of a line)

Semicolons are really only needed when multiple statements occur in the same line:var a = getAnimal(); println(a)

实际上,仅当在同一行中出现多个语句时才需要分号: var a = getAnimal(); println(a) var a = getAnimal(); println(a)

We can, and almost always do, omit semicolons at the end of lines:var a = getAnimal()

我们可以并且几乎总是这样做,在行尾省略分号: var a = getAnimal()

方法和函数用'def'关键字声明 (Methods and functions are declared with the ‘def’ keyword)

Methods and functions (a function is like a method, but doesn’t belong to any class) are declared with the def keyword. Their return types are declared after the function name and a colon. And their bodies typically come after an equals sign:

方法和函数( 函数类似于方法 ,但不属于任何类)使用def关键字声明。 它们的返回类型在函数名称和冒号之后声明。 他们的身体通常在等号后出现:

def getAnimal(): Animal = {
// body goes here ...
}

调用无参数方法或函数时,可以省略括号 (When calling a no-argument method or function, the parenthesis can be omitted)

In Java, we always provide the parentheses when invoking a method:

在Java中,我们总是在调用方法时提供括号:

Animal a = getAnimal();

When calling a Scala function or method that takes no arguments, the parenthesis can be omitted:

当调用不带参数的Scala函数或方法时,可以省略括号:

val a = getAnimal()
// or
val a = getAnimal

方法和函数无需显式返回任何内容 (Methods and functions do not need to explicitly return anything)

The last value or expression in a method or function becomes the implicit return value:

方法或函数中的最后一个值或表达式成为隐式返回值:

def add(x: Int, y: Int): Int = {
x + y // the sum of x and y will be implicitly be returned
}

So while the return keyword exists in Scala, it is almost never used.

因此,尽管return关键字存在于Scala中,但几乎从未使用过。

单位表示无效 (Unit means void)

If a Java method has no return value, then it is declared as returning void:

如果Java方法没有返回值,则将其声明为返回void

public void output(String s) {
System.out.println(s);
}

In Scala, the return type would be declared as Unit:

在Scala中,返回类型将声明为Unit

def output(s: String): Unit = {
println(s)
}

方法和函数很少需要声明其返回类型 (Methods and functions rarely need to declare their return type)

Scala’s strong type inference means that we rarely need to declare the return type of our functions/methods:

Scala的强类型推断意味着我们几乎不需要声明函数/方法的返回类型:

def add(x: Int, y: Int) = {
x + y // The compiler infers that an Int will be returned
}

Note that we still need to declare the return type in cases where:

请注意,在以下情况下,我们仍然需要声明返回类型:

  • the method is abstract

    该方法是抽象的
  • we want to return a superclass of the inferred return type

    我们要返回推断的返回类型的超类
  • we simply want to be more clear

    我们只是想更清楚
def getAnimal(): Animal {
// if we omitted the return type, Scala would infer Dog
new Dog()
}

方法和功能不需要大括号 (Methods and functions do not require braces)

If the body of a method or function does not span more than one line (or expression), then the braces can be omitted:

如果方法或函数的主体不超过一行(或表达式),则可以省略花括号:

def add(x: Int, y: Int) = 
x + y// ordef add(x: Int, y: Int) = x + y

可以使用命名参数调用方法和函数 (Methods and functions can be invoked with named arguments)

In Java, when invoking a method that accepts multiple parameters, we cannot include the parameter names. Instead, we simply pass the values, in the order in which they are declared in the method:

在Java中,当调用接受多个参数的方法时,我们不能包含参数名称。 相反,我们只是按照在方法中声明它们的顺序传递值:

int i = add(6, 8);

In Scala, we can choose to either call a method/function the “Java way”, or we can include all, or some, of the parameter names:

在Scala中,我们可以选择以“ Java方式”调用方法/函数,也可以包含全部或部分参数名称:

val i = add(6, 8)
// or
val i = add(x = 6, y = 8)
// or
val i = add(x = 6, 8)
// note: below will not compile:
val i = add(6, y = 8)

特性而不是接口 (Traits instead of interfaces)

While Java offers interfaces, Scala offers traits. Like Java interfaces, traits can include abstract methods. And like Java interfaces’ default methods, traits can offer implemented methods. Unlike interfaces, traits can also include variables.

Java提供接口,而Scala提供traits 。 像Java接口一样,特征可以包含抽象方法。 与Java接口的默认方法一样,特征可以提供已实现的方法。 与接口不同,特征还可以包含变量。

trait Shape {
val point: Point
def print() // no implementation, so implicitly abstract
}

`extends`和`with` (`extends` and `with`)

As in Java, Scala classes cannot extend multiple parent classes. Also similar to Java, Scala classes can implement (or in Scala parlance, extend) multiple traits.

与Java中一样,Scala类不能扩展多个父类。 同样类似于Java,Scala类可以实现(或以Scala的话来说, extend )多个特征。

When a Scala class extends either a class or a trait, the extends keyword is used. If the class then extends any additional traits, then the with keyword is used:

当Scala类扩展类或特征时,将使用extends关键字。 如果该类随后扩展了任何其他特征,则使用with关键字:

class Shape { ... }trait Drawable { ... }trait Movable { ... }class Circle extends Shape with Drawable with Movableclass Raster extends Drawable with Movable

声明类时,其成员立即在括号中列出 (Classes are declared with their members immediately listed in parenthesis)

Whereas we might define a simple class in Java like this:

而我们可能会像这样在Java中定义一个简单的类:

public class Foo {
private String a;
private int b;
// getters, setters, constructors, etc
}

We would typically define a Scala class all in one line, like so:

我们通常会在一行中全部定义一个Scala类,如下所示:

class Foo(a: String, b: Int) { }

Brackets are optional, unless additional members, methods, etc, need to be declared:

括号是可选的,除非需要声明其他成员,方法等:

class Foo(a: String, b: Int)

案例类代表专门的数据类 (Case classes represent specialized data classes)

Java 14 introduced the concept of records. If you’re familiar with records, then you’ll quickly become familiar with case classes. Otherwise, case classes are special data classes. Their syntax is similar to regular classes:

Java 14引入了记录的概念。 如果您熟悉记录,那么您将很快熟悉案例类。 否则,案例类是特殊的数据类。 它们的语法类似于常规类:

case class Foo(a: String, b: Int)

However, the compiler auto-generated the standard methods required of a data class, such as equals(), hashCode() and toString() methods, getters, etc.

但是,编译器会自动生成数据类所需的标准方法,例如equals()hashCode()toString()方法,getter等。

It also generates a “companion object” with an apply() and unapply() method (we’ll discuss these a bit later).

它还会使用apply()unapply()方法生成一个“伴侣对象”(我们将在后面讨论)。

泛型类型用方括号( [] )表示,而不用尖括号( <> )表示 (Generic types are denoted by square brackets ([]), rather than angle brackets (<>))

In Java, we denote a generic type using angle brackets:List<String> list;

在Java中,我们使用尖括号表示通用类型: List<String> list;

In Scala, square brackets are used to denote generic types:List[String] list

在Scala中,方括号用于表示通用类型: List[String] list

字符串插值是通过以`s'为前缀的字符串文字完成的 (String interpolation is done with String literals prefixed by `s`)

In Java, to quickly concatenate Strings, we use the plus (+) symbol:System.out.println("Hello, "+ firstName + " "+ lastName + "!");

在Java中,为了快速连接字符串,我们使用加号(+): System.out.println("Hello, "+ firstName + " "+ lastName + "!");

In Scala, we perform String interpolation by prefixing a String literal with s, and by referring to variables with the dollar sign ($):println(s"Hello, $firstName $lastName!")

在Scala中,我们通过在字符串文字前加上s并引用带有美元符号( $ )的变量来执行字符串插值: println(s"Hello, $firstName $lastName!")

To access a nested value, or an expression, curly braces are used:

要访问嵌套值或表达式,请使用花括号:

println(s"Hello, ${name.first} ${name.middle.getOrElse("")} ${name.last}!")

匿名函数看起来类似于Java lambda,但带有=>符号 (Anonymous functions look similar to Java lambdas, but with the => symbol)

Java provides syntactic sugar when constructing lambda functions:list.foreach(item -> System.out.println("* " + item));

Java在构造lambda函数时提供了语法糖: list.foreach(item -> System.out.println("* " + item));

In Scala, we use the => symbol with anonymous functions:list.foreach(item => println(s"* $item"))

在Scala中,我们使用=>符号和匿名函数: list.foreach(item => println(s"* $item"))

下划线充当占位符 (Underscores serve as a placeholder)

Underscores are used by Scala as placeholders. You’ll see underscores in a few different use cases, including:

下划线由Scala用作占位符。 您将在一些不同的用例中看到下划线,包括:

Anonymous functions:Rather than declaring a variable in the anonymous function:list.foreach(item => println(item))

匿名函数:而不是在匿名函数中声明变量: list.foreach(item => println(item))

We can omit the item variable and replace it with an underscore:list.foreach(println(_))

我们可以省略item变量,并用下划线替换: list.foreach(println(_))

Note, similar to Java, we can simplify the line above by providing a reference to the println function (which will implicitly take the current variable as an argument):list.foreach(println)

注意,类似于Java,我们可以通过提供对println函数的引用(将隐式地将当前变量作为参数)引用来简化上面的代码: list.foreach(println)

Pattern matching:When we perform pattern matching, we are attempting to match an instance with its type. Say we have a case class like so:

模式匹配:执行模式匹配时,我们试图将实例与其类型匹配。 假设我们有一个案例类,如下所示:

case class Dog(name: String) extends Animal

If we have an instance a of an Animal, we may try to determine if it is a Dog with a particular name:

如果我们有动物的实例a ,我们可以尝试确定它是否是具有特定名称的狗:

a match {
case Dog(name) => println(s"Found a dog named $name");
...
}

Or, we might not care about the Dog’s name. Dog’s constructor still takes an argument, so we can simply use the underscore as a placeholder:

或者,我们可能不在乎狗的名字。 Dog的构造方法仍然需要一个参数,因此我们可以简单地使用下划线作为占位符:

a match {
case Dog(_) => println(s"Found some dog");
...
}

Likewise, we might want to know if we’ve found a Dog, or anything other than a Dog. Again we’d use an underscore:

同样,我们可能想知道是否找到了Dog或除Dog以外的任何东西。 同样,我们将使用下划线:

a match {
case Dog(_) => println(s"Found a dog");
case _ => println("Found something other than a dog")
}

随播对象而不是静态对象 (Companion objects instead of statics)

Java has the static keyword, which describes variables and methods that are tied to a particular class, rather than instances of an object.

Java具有static关键字,该关键字描述与特定 (而不是对象的实例)相关联的变量和方法。

public class UserManager {
public static void save(User u) {
//...
}
}// we can invoke that static method via:UserManager.save(new User("Pat"));

Scala does not offer the static keyword. However, with Scala, we can create objects, which effectively offer the same thing:

Scala不提供static关键字。 但是,使用Scala,我们可以创建对象,有效地提供相同的功能:

object User {
def save(u: User) = {
// ...
}
}// we can invoke that object's method via:User.save(new User("Pat"))

Moreover, Scala allows us to create “companion objects”; that is, objects that correspond with identically-named classes:

而且,Scala允许我们创建“伴侣对象”。 也就是说,与名称相同的相对应的对象

class User(name: String) {
}// This is the companion object to the User class
object User {
def apply() {
new User("Noname")
}
def apply(name: String) = {
new User(name)
}
def unapply(u: User) = {
Some(u.name)
}
def save(u: User) = {
// ...
}
}

While we can define any functions in a class’ companion object, the apply() and unapply() methods have special meaning.

虽然我们可以在类的伴随对象中定义任何函数,但是apply()unapply()方法具有特殊的含义。

If we were to call the object’s name, followed by parenthesis with any number of arguments, the corresponding apply(…) method in the object would be invoked.

如果要调用对象的名称,然后调用带有任意数量参数的括号,则将调用对象中的相应apply(…)方法。

In the above example, invoking User() would, under the covers, cause User.apply() to be invoked; invoking User("Chris") would actually invoke User.apply("Chris"). Commonly, an instance of the companion class is constructed and returned. In this manner, a companion object’s apply() method(s) can — and typically are — used as factory methods.

在上面的示例中,在User.apply()调用User()将导致User.apply()被调用; 调用User("Chris")实际上会调用User.apply("Chris") 。 通常,将构造并返回伴随类的实例。 通过这种方式,伴侣对象的apply()方法可以(通常是)用作工厂方法。

For this reason, we will typically see objects being created like this:

因此,我们通常会看到这样创建的对象:

val u = User("Suzie")

instead of this:

代替这个:

val u = new User("Suzie")

On the other hand, unapply() is used as a deconstructor. Whenever the individual components of a class are needed (such as during pattern matching), its unapply() method is invoked behind the scenes.

另一方面, unapply()用作解构函数。 每当需要类的各个组件时(例如在模式匹配期间),都会在后台调用其unapply()方法。

一个综合的例子 (A comprehensive example)

Let’s analyze a block of Scala code based on what we’ve just gone over:

让我们根据刚刚介绍的内容来分析Scala代码块:

case class Point(x: Int, y: Int)  // 1trait Drawable {  // 2
def draw(): Unit // 3
}abstract class Shape(val center: Point) { // 4
def area(): Double
}case class Circle(override val center: Point, radius: Int) extends Shape(center) with Drawable { // 5
override def draw(): Unit = { // 6
println(s"⊙ ")
}
override def area() = Math.PI * (radius * radius)
def growBy(add: Int) = Circle(center, radius + add) // 7
}case class Square(override val center: Point, side: Int) extends Shape(center) with Drawable {
override def draw(): Unit = {
println("◼︎")
}
override def area() = Math.PI * (side * side)
}object Canvas { // 8
def main(args: Array[String]): Unit = { // 9
val circle1 = Circle(Point(50, 25), radius = 10) // 10
val circle2 = circle1.growBy(5)
val square = Square(Point(100, 150), side = 30)
val l: List[Drawable] = List(circle1, circle2, square) // 11
l.foreach(_.draw()) // 12
l.map(d => d match { // 13
case Circle(_, r) => s"Circle of radius $r" // 14
case
Square(_, s) => s"Square of side length $s"
case
_ => "Unknown shape" // 15
}).foreach(println) // 16
}
}
  1. We define a case class, Point. The compiler creates a bunch of boilerplate for us behind the scenes, including a Point companion class.

    我们定义一个案例类Point 。 编译器在幕后为我们创建了一堆样板,包括Point伴随类。

  2. We define a trait, Drawable, with…

    我们用…定义一个特征Drawable

  3. …an implicitly abstract method, draw(). Since the method is abstract, we specify the return type as Unit (which means nothing will be returned)

    …一个隐式抽象的方法draw() 。 由于该方法是抽象方法,因此我们将返回类型指定为Unit (这意味着将不返回任何内容)

  4. We define an abstract class, Shape, which declares a val, center, and an implicitly abstract method, area()

    我们定义一个抽象类Shape ,它声明一个valcenter和一个隐式抽象方法area()

  5. We define a case class, Circle, which extends the Shape class and the Drawable trait.

    我们定义了一个Case类Circle ,它扩展了Shape类和Drawable特征。

  6. We use the override keyword to implement the abstract draw() method of the parent Shape class. Here, we choose to enclose the method’s body in curly brackets.

    我们使用override关键字来实现父Shape类的抽象draw()方法。 在这里,我们选择将方法的主体括在大括号中。

  7. We then define two more methods: the area() method overridden from the parent Shape class, and a new growBy() method that returns a new Circle instance. In both bases, we omit the curly brackets from the method body.

    然后,我们定义另外两个方法:从父Shape类重写的area()方法和一个返回新Circle实例的新growBy()方法。 在这两个基础中,我们都省略了方法主体中的花括号。

  8. We create a Canvas object. All of its functions can be thought of as similar to Java static methods.

    我们创建一个Canvas对象。 可以认为它的所有功能都类似于Java 静态方法。

  9. We can define a main method, just like in Java. Being in an object, this method is static and could be invoked like Canvas.main(Array()).

    就像在Java中一样,我们可以定义一个main方法。 在对象中,此方法是静态的,可以像Canvas. main ( Array ())一样调用Canvas. main ( Array ()) Canvas. main ( Array ())

  10. We create a Circle instance using the syntactic sugar which invokes the Circle companion object’s apply() method. Recall that the Circle companion object, and its apply() method, are generated by the compiler because Circle is a case class.

    我们使用语法糖创建一个Circle实例,该语法糖调用Circle伴侣对象的apply()方法。 回想一下, Circle伴随对象及其apply()方法是由编译器生成的,因为Circle是一个case类。

  11. We create a List of Drawables, using square brackets to indicate the List’s generic type.

    我们创建一个DrawableList ,使用方括号指示List的泛型类型。

  12. We use the List’s foreach method to iterate over its Drawables, calling each’s draw() method. For brevity, we use the underscore character to represent the current Drawable.

    我们使用Listforeach方法遍历其Drawable ,调用它们的draw()方法。 为简洁起见,我们使用下划线字符表示当前的Drawable

  13. We then use the List’s map method to convert each Drawable into a String value.

    然后,我们使用Listmap方法将每个Drawable转换为String值。

  14. We use pattern matching to determine the specific type of Drawable we’re iterating over. On this line, we’re looking for a Circle with any center value (represented by the underscore character), and are capturing the radius value with the r variable. We then use String interpolation to create a String containing that r value. We do something similar when matching a Square on the line below.

    我们使用模式匹配来确定要迭代的Drawable的特定类型。 在这条线上,我们正在寻找一个具有任何中心值的Circle (用下划线字符表示),并使用r变量捕获半径值。 然后,我们使用字符串插值创建包含该r值的String 。 当匹配下面一行的Square时,我们会做类似的事情。

  15. We capture cases where we match a Drawable that is neither a Circle or a Square by using the underscore character.

    通过使用下划线字符,我们捕获了匹配不为CircleSquareDrawable情况。

  16. We then call foreach on the resulting List of Strings, and reference the println() function, which is implicitly invoked with the String being currently iterated over.

    然后,我们在生成的Strings List上调用foreach ,并引用println()函数,该函数通过当前正在迭代的String隐式调用。

翻译自: https://medium.com/the-innovation/scala-syntax-for-java-developers-69734ce17cdf

scala语法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值