Scala против Java: зачем мне изучать Scala?

Опубликовано: 2022-03-11

По общему признанию, есть доля правды в утверждении, что «Scala — это сложно», но кривая обучения стоит вложений. Некоторые из более сложных функций языка (кортежи, функции, макросы и многие другие) в конечном счете облегчают разработчику написание более качественного кода и повышают производительность за счет программирования на 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 имеет несколько более сложную структуру. Но если вы можете написать чуть более сложную одну строку кода, которая заменит 20 «более простых» строк Java, какая из них действительно сложнее?

Правда в том, что Java часто слишком многословна. В Scala компилятор невероятно умен, поэтому разработчику не нужно явно указывать те вещи, которые может вывести компилятор. Сравните, например, это простое «Hello World!» программа на Java против Scala:

Привет, мир на Java:

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

Привет, мир на Scala:

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

Хотя здесь нет большой разницы между двумя языками, Scala менее многословен даже в этом простом примере.

В качестве более практического примера давайте рассмотрим создание простого списка строк:

Джава:

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

Скала:

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

Конечно, в 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 против Scala

Давайте сделаем еще один шаг и сравним создание стандартного bean-компонента/обычного старого Java-объекта (POJO) в Java и Scala.

Во-первых, версия 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; } }

Фу. Лотта код.

Теперь версия 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, а затем избавиться от геттеров и сеттеров.

Однако, если вы вернетесь к рассуждениям о геттерах и сеттерах в Java, это специально для будущего. То есть, если вам позже потребуется добавить некоторую логику либо к получению, либо к установке переменных, вам придется переписать эти общедоступные переменные, чтобы вместо этого использовать методы (именно поэтому использование геттеров и сеттеров для начала рекомендуется в Java ). Однако в программировании на Scala это не так. Из-за дизайна языка абстракция остается неизменной без необходимости использования геттеров и сеттеров. Рассмотрим, например, этот модифицированный класс User в Scala, который NullPointerException , если вы пытаетесь установить для имени значение null:

 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 предпочитает неизменяемость, я могу написать это на Scala еще более лаконично с case-классами:

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

С ума сойти, насколько меньше кода мне приходится писать.

Взяв пример немного дальше

Теперь рассмотрим сценарий с вышеуказанными классами, где я хочу добавить изящный маленький метод в класс User , который возвращает список всех Products , заказанных 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 , иначе getProducts() в Java был бы еще длиннее.

С другой стороны, в Scala все, что нам нужно, это:

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

Вы можете видеть, насколько меньше реализация языка Scala. Да, новичку в Scala это может показаться более сложным, но как только вы действительно полностью поймете лежащие в его основе концепции, код Scala будет выглядеть намного проще, чем код Java.

Давайте здесь еще немного усложним. Что, если мы хотим получить Products только из определенной Category ?

В этом случае мы не можем воспользоваться методом addAll в java.util.List , поэтому в 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?»

Связанный: Сокращение стандартного кода с помощью макросов Scala и квазицитатов