Scala与Java:为什么要学习Scala?

本文概述

诚然, ” Scala很难”这一说法确实有些道理, 但是学习曲线很值得投资。该语言的某些更复杂的功能(例如, Tuples, Functions, Macros等)最终使开发人员可以更轻松地编写更好的代码并通过在Scala中进行编程来提高性能。坦白地说, 我们是程序员, 如果我们不够聪明, 无法学习某种复杂的语言, 那么我们就做错了事。

Scala是一种类型安全的JVM语言, 它将面向对象的程序和函数式编程都结合到一种极其简洁, 逻辑且功能强大的语言中。有些人可能会惊讶地发现Scala并不像他们想象的那么新, 它于2003年首次推出。但是, 尤其是在过去的几年中, Scala才开始获得大量关注。这就引出了”为什么要使用Scala?”的问题。

本文研究了Scala的优势, 尤其是与Java相比(因为Scala编写为在JVM中运行)。 Scala不是创建”更好的Java”的唯一尝试。诸如Kotlin和Ceylon之类的替代方案也走了这条路, 但他们做出了一个基本决定, 即在语法上保持与Java语言本身非常接近, 以最大程度地减少学习时间。这似乎是一个不错的主意, 但最终还是有些自欺欺人, 因为它迫使你停留在许多完全相同的Java范式中, 这些范式就是首先要创建”更好的Java”的原因。

相比之下, Scala的创建目的是成为一种更好的语言, 以摆脱Java认为对开发人员来说过于严格, 过于繁琐或令人沮丧的那些方面。结果, 确实存在代码差异和范式转换, 这可能会使Scala编程的早期学习变得更加困难, 但是结果是一种更加简洁和井井有条的语言, 最终更易于使用并提高了生产率。

并排比较Scala与Java,Scala的效率显而易见。

Scala与Java:哪个更复杂?

具有讽刺意味的是, 虽然Java语言的简单性是其成功的一部分, 但它也为其复杂性做出了贡献。当然, 你几乎可以用Java编写任何东西, 但是这样做所需的代码行可能令人生畏。另一方面, Scala中的编程结构稍微复杂一些。但是, 如果你可以编写稍微复杂一点的单行代码来代替Java的20条”更简单”的行, 那么哪一行真的更复杂?

事实是Java通常太冗长了。在Scala中, 编译器非常聪明, 因此避免了开发人员需要明确指定编译器可以推断的内容。比较一下这个简单的” Hello World!” Java vs. Scala中的程序:

Java中的Hello World:

public class HelloJava {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

斯卡拉的Hello World:

object HelloScala {
    def main(args: Array[String]): Unit = {
        println("Hello World!")
    }
}

尽管此处的两种语言之间并没有很大的区别, 但即使在这个简单的示例中, Scala的详细程度也较低。

举一个更实际的例子, 让我们看一下创建一个简单的字符串列表:

Java:

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");

规模:

val list = List("1", "2", "3")

当然, Java中有一些技巧可以缩短代码, 但在标准用法中却不行。

现在考虑一种情况, 我们有一个数字字符串列表, 但我们希望将该列表转换为整数列表:

Java:

List<Integer> ints = new ArrayList<Integer>();
for (String s : list) {
    ints.add(Integer.parseInt(s));
}

规模:

val ints = list.map(s => s.toInt)

借助Scala的功能特性, 这种转换变得非常简单。

一个类示例:Java vs. Scala

让我们更进一步, 比较Java和Scala中的标准Bean /普通旧Java对象(PO​​JO)创建。

首先, Java版本:

public class User {
    private String name;
    private List<Order> orders;
    
    public User() {
        orders = new ArrayList<Order>();
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public List<Order> getOrders() {
        return orders;
    }
    
    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }
}

public class Order {
    private int id;
    private List<Product> products;
    
    public Order() {
        products = new ArrayList<Product>();
    }
    
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public List<Product> getProducts() {
        return products;
    }
    
    public void setProducts(List<Product> products) {
        this.products = products;
    }
}

public class Product {
    private int id;
    private String category;
    
    public int getId() {
        return id;
    }
    
    public void setId(int id) {
        this.id = id;
    }
    
    public String getCategory() {
        return category;
    }
    
    public void setCategory(String category) {
        this.category = category;
    }
}

ew Lotta代码。

现在是Scala版本:

class User {
    var name: String = _
    var orders: List[Order] = Nil
}

class Order {
    var id: Int = _
    var products: List[Product] = Nil
}

class Product {
    var id: Int = _
    var category: String = _
}

我们说哪种语言比较复杂?

我们公平吗?

如果你到现在为止都是Java程序员, 那么你可能会认为我在对代码进行不公平的比较。毕竟, 没有什么能阻止我使用Java创建公共变量, 然后摆脱getter和setter。

但是, 如果你回想一下Java中的getter和setter背后的原因, 那么它专门用于将来的验证。也就是说, 如果以后需要在获取或设置变量时添加一些逻辑, 则必须重新编写那些公共变量以使用方法(这就是Java中鼓励使用getter和setter开头的原因。 )。但是, 在Scala编程中并非如此。由于采用了语言设计, 因此抽象无需使用getter和setter即可保持完整。例如, 考虑一下Scala中经过修改的User类, 如果你尝试将名称设置为null, 则会抛出NullPointerException:

class User {
    private var _name: String = _
    var orders: List[Order] = Nil
    def name = _name
    def name_=(name: String) = {
        if (name == null) {
            throw new NullPointerException("User.name cannot be null!")
        }
        _name = name
    }

你仍然可以像这样设置名称:

user.name = "John Doe"

请注意, 这完全消除了预先配置方法访问器的需要。

而且, 由于Scala更喜欢不变性, 因此我可以使用case类更简洁地在Scala中编写此代码:

case class User(name: String, orders: List[Order])
case class Order(id: Int, products: List[Product])
case class Product(id: Int, category: String)

太疯狂了, 我要写的代码少了多少。

进一步举例说明

现在考虑上述类的场景, 我想在User类中添加一个漂亮的小方法, 该方法返回用户订购的所有产品的列表:

在Java的冗长世界中:

public List<Product> getProducts() {
    List<Product> products = new ArrayList<Product>();
    for (Order order : orders) {
        products.addAll(order.getProducts());
    }
    return products;
}

幸运的是, java.util.List有一个addAll方法, 否则在Java中getProducts()可能更长。

另一方面, 在Scala中, 我们需要做的是:

def products = orders.flatMap(o => o.products)

你会看到Scala语言实现的规模要小得多。是的, 对于Scala新手来说, 它似乎更加复杂, 但是一旦你真正理解了其背后的概念, Scala代码将比Java代码看起来更加简单。

让我们变得更加复杂。如果我们只想获取特定类别中的产品怎么办?

在这种情况下, 我们无法利用java.util.List中的addAll方法, 因此Java中的情况变得更糟:

public List<Product> getProductsByCategory(String category) {
    List<Product> products = new ArrayList<Product>();
    for (Order order : orders) {
        for (Product product : order.getProducts()) {
            if (category.equals(product.getCategory())) {
                products.add(product);
            }
        }
    }
    return products;
}

但是, 在Scala中, 代码仍然相当简单。我们只需要使用flatMap即可将拼合在一起的每个Order的产品列表合并到一个列表中, 然后进行过滤以仅包括与类别匹配的产品:

def productsByCategory(category: String) = orders.flatMap(o => o.products).filter(p => p.category == category)

动态与静态

在过去的几年中, 当然不缺少新语言, 但是尽管最近出现的几乎所有其他语言都是动态的, 但是Scala是静态类型的。

作为专业的开发人员(尽管我知道并使用许多动态语言), 我认为编译时检查对于编写可靠的代码非常重要。在动态语言中, 除非你在各种情况下实际运行它, 否则你将永远无法确保自己的代码没有足够的错误和健壮性。这可能导致代码中潜在的严重缺陷, 直到代码投入生产后才可能实现。

本文总结

希望本文将Java与Scala进行了充分的融合, 以使你对Scala的功能和能力有初步的了解, 并激发了你学习该语言的兴趣。它不仅是一种很棒的语言, 它可以使编程变得不那么乏味并且更加有趣, 而且还被世界上一些最大的公司(LinkedIn, Twitter, FourSquare, The Guardian等)使用。

Scala开发人员的空缺职位数量不断增加, 证明了Scala的受欢迎程度和使用量正在迅速上升。如果你还没有这样做的话, 那么现在将是一个开始尝试并停止询问”为什么要学习Scala?”的好时机。

微信公众号
手机浏览(小程序)
0
分享到:
没有账号? 忘记密码?