No way back: Why I switched from Java to Scala and am not going to return

    The debate over the advantages and disadvantages of Scala over Java reminds me of the debate about C versus C ++. Pluses, of course, are an order of magnitude more complex language with a huge number of ways to shoot yourself in the foot, drop an application or write completely unreadable code. But, on the other hand, C ++ is simpler. It allows you to do just what it would be on a bare C is difficult . In this article, I will try to talk about the side of Scala that made this language industrial - that makes programming easier and the source code clearer.

    Further comparisons between languages ​​come from the fact that the reader is familiar with the following things:

    - Java8. There is nothing to talk about without lambd support
    - LombokShort annotations instead of long sheets of getters, setters, constructors and builders
    - Guava Immutable collections and transformations
    - Java Stream API
    - A decent framework for SQL, so multiline strings support is not needed
    - flatMap - map, replacing an element with an arbitrary amount (0, 1, n) of other elements.

    Default immunity


    Probably everyone already agrees that immutable data structures are a Good Idea. Scala allows you to write immutable code without setting the `final`

    Java
    @Value
    class Model {
        String s;
        int i;
    }
    public void method(final String a, final int b) {
      final String c = a + b;
    }
    


    Scala
    case class Model(s: String, i: Int)
    def method(a: String, b: Int): Unit = {
      val c: String = a + b
    }
    


    Code block, condition, switch are an expression, not an operator


    Those. all of the above returns a value, allowing you to get rid of the return statement and greatly simplifying code that works with immutable data or a large number of lambdas.

    Java
    
    final String s;
    if (condition) {
      doSomething();
      s = "yes";
    } else {
      doSomethingElse();
      s = "no"
    }
    


    Scala
    val s = if (condition) {
      doSomething();
      "yes"
    } else {
      doSomethingElse();
      "no"
    }
    


    Pattern matching, unapply () and sealed class hierarchies


    Have you ever wanted to have a switch that works with arbitrary data types, displays a warning when compiling, if it does not cover all possible cases, and also knows how to make selections according to difficult conditions, and not according to the fields of an object? In Scala he is!

    Scala
      sealed trait Shape  //sealed trait - интерфейс, все реализации которого должны быть объявлены в этом файле
      case class Dot(x: Int, y: Int) extends Shape
      case class Circle(x: Int, y: Int, radius: Int) extends Shape
      case class Square(x1: Int, y1: Int, x2: Int, y2: Int) extends Shape
      val shape: Shape = getSomeShape() //объявляем локальную переменную типа Shape
      val description = shape match {
          //x и x в выражении ниже - это поля объекта Dot
        case Dot(x, y) => "dot(" + x + ", " + y + ")"
          //Circle, у которого радиус равен нулю. А также форматирование строк в стиле Scala
        case Circle(x, y, 0) => s"dot($x, $y)"
          //если радиус меньше 10
        case Circle(x, y, r) if r < 10 => s"smallCircle($x, $y, $r)"
        case Circle(x, y, radius) => s"circle($x, $y, $radius)"
          //а прямоугольник мы выбираем явно по типу
        case sq: Square => "random square: " + sq.toString
      } //если вдруг этот матч не охватывает все возможные значения, компилятор выдаст предупреждение
    


    Java
    I will not even try to repeat it in Java.

    A set of syntax features to support composition


    If the first three whales of OOP are (we say in chorus) encapsulation, polymorphism and inheritance, and the fourth is aggregation, then the fifth whale will undoubtedly be a composition of functions, lambdas and objects.

    What is the problem of Java? In parentheses. If you do not want to write single-line methods, then when calling a method with a lambda, you will have to wrap it in addition to the parentheses of the method call.

    Java
    
    //допустим у нас есть библиотека иммутабельных коллекций с методами map и flatMap. Для другой библиотеки коллекций это будет еще больше кода.
    //в collection заменить каждый элемент на ноль, один или несколько других элементов, вычисляемых по алгоритму
    collection.flatMap(e -> {
      return getReplacementList(e).map(e -> {
        int a = calc1(e);
        int b = calc2(e);
        return a + b;
      });
    });
    withLogging("my operation {} {}", a, b, () -> {
      //do something
    });
    


    Scala
    collection.flatMap { e =>
      getReplacementList(e).map { e =>
        val a = calc1(e)
        val b = calc2(e)
        a + b
      }
    }
    withLogging("my operation {} {}", a, b) {
      //do something
    }
    


    The difference may seem insignificant, but with the massive use of lambdas it becomes significant. Like using lambdas instead of inner classes. Of course, this requires the availability of appropriate libraries designed for the mass use of lambdas - but they certainly already exist or will be available soon.

    Method parameters: named parameters and default parameters


    Scala allows you to explicitly specify argument names when calling methods, and also supports default argument values. Have you ever written converters between domain models? This is how it looks in the rock:

    Scala
    def convert(do: PersonDataObject): Person = {
      Person(
        firstName = do.name,
        lastName = do.surname,
        birthDate = do.birthDate,
        address = Address(
          city = do.address.cityShort,
          street = do.address.street
        )
      )  
    


    The set of parameters and their types are controlled at the compilation stage, in runtime it is just a call to the constructor. In Java, however, you have to use either a call to the constructor / factory method (lack of control over the arguments, mixed up two string arguments and hello in places), or builders (almost good, but the fact that all the necessary parameters were specified when constructing the object can only be checked in runtime )

    null and NullPointerException


    Skalovsky `Option` is basically no different from the Java` Optional`, but the above features make working with it easy and enjoyable, while in Java you have to make some efforts. Rock programmers don't need to force themselves to avoid nullable fields - a wrapper class is just as convenient as null.

    Scala
    val value = optValue.getOrElse("no value") //значение или строка "no value"
    val value2 = optValue.getOrElse {  //значение или exception
      throw new RuntimeException("value is missing")
    }
    val optValue2 = optValue.map(v => "The " + v) //Option("The " + value)
    val optValue3 = optValue.map("The " + _) //то же самое, сокращенная форма
    val sumOpt = opt1.flatMap(v1 => opt2.map(v2 => v1 + v2)) //Option от суммы значений из двух других Option
    val valueStr = optValue match { //Option - это тоже sealed trait с двумя потомками!
      case Some(v) =>  //сделать что-то если есть значение, вернуть строку
        log.info("we got value {}", v)
        "value.toString is " + v
      case None => //сделать что-то если нет значения, вернуть другую строку
        log.info("we got no value")
        "no value"
    }
    


    Of course, this list is not complete. Moreover, each example may seem insignificant - well, what, in fact, is the difference, how many brackets will have to be written when calling a lambda? But the key advantage of the rock is the code that is obtained by combining all of the above. So java5 from java8 is not very different in terms of syntax, but a set of minor changes makes development much easier, including opening up new possibilities in the architectural plan.

    Also, this article does not cover other powerful (and dangerous) features of the language, the Scala ecosystem, and the FP as a whole. And nothing is said about the flaws (who do not have them ...). But I hope that the Javists will get an answer to the question “Why is this rock needed”, and the rockers will be better able to defend the honor of their language in online battles)

    Also popular now: