Подтвердить что ты не робот

Борьба с привычками, создаваемыми Java при переносе на Scala

Каковы наиболее распространенные ошибки, которые делают разработчики Java при переносе на Scala?

По ошибкам я имею в виду писать код, который не соответствует духу Scala, например, используя циклы, когда подобные карте-функции более подходят, чрезмерное использование исключений и т.д.

EDIT: еще один использует собственные getters/seters вместо методов, любезно генерируемых Scala

4b9b3361

Ответ 1

Один из очевидных заключается в том, чтобы не использовать вложенное определение области, которое допускает scala, плюс задержка побочных эффектов (или понимание того, что все в scala является выражением):

public InputStream foo(int i) {
   final String s = String.valueOf(i);
   boolean b = s.length() > 3;
   File dir;
   if (b) {
       dir = new File("C:/tmp");
   } else {
       dir = new File("/tmp");
   }
   if (!dir.exists()) dir.mkdirs();
   return new FileInputStream(new File(dir, "hello.txt"));
}

Может быть преобразован как:

def foo(i : Int) : InputStream = {
   val s = i.toString
   val b = s.length > 3
   val dir = 
     if (b) {
       new File("C:/tmp")
     } else {
       new File("/tmp")
     }
   if (!dir.exists) dir.mkdirs()
   new FileInputStream(new File(dir, "hello.txt"))
}

Но это может быть значительно улучшено. Это может быть:

def foo(i : Int) = {
   def dir = {
     def ensuring(d : File) = { if (!d.exists) require(d.mkdirs); d }
     def b = { 
       def s = i.toString
       s.length > 3
     }
     ensuring(new File(if (b) "C:/tmp" else "/tmp"));
   }
   new FileInputStream(dir, "hello.txt")
}

Последний пример не "экспортирует" любую переменную за пределы области действия, которая необходима. Фактически, он вообще не объявляет никаких переменных. Это означает, что позже будет проще реорганизовать. Конечно, этот подход действительно приводит к чрезвычайно раздутым файлам классов!

Ответ 2

Это довольно просто: Java-программист будет писать императивный код стиля, тогда как более Scala -образный подход будет включать функциональный стиль.

Ответ 3

Несколько моих фаворитов:

  • Мне потребовалось некоторое время, чтобы понять, насколько действительно полезен вариант. Общей ошибкой, которую несет Java, является использование null для представления поля/переменной, которое иногда не имеет значения. Признайте, что вы можете использовать "map" и "foreach" в опции для записи более безопасного кода.

  • Узнайте, как использовать "map", "foreach", "dropWhile", "foldLeft",... и другие полезные методы в коллекциях Scala для сохранения написания типов конструкций контуров, которые вы видите повсюду в Java, который я теперь воспринимаю как подробный, неуклюжий и трудный для чтения.

Ответ 4

Общей ошибкой является дикая и чрезмерная функция, не присутствующая на Java, как только вы ее "grokked". Например. новички, как правило, злоупотребляют сопоставлением шаблонов (*), явной рекурсией, неявными преобразованиями, (псевдо) операцией перегрузки и т.д. Другая ошибка заключается в неправильном использовании функций, внешне похожих на Java (но не таких), как для понятий или даже if (что больше похоже на Java-тернарный оператор ?:).

(*) Существует большой лист читов для сопоставления шаблонов на Option: http://blog.tmorris.net/scalaoption-cheat-sheet/

Ответ 5

Я до сих пор не принимал ленивых денег и потоков.

В начале общая ошибка (которую обнаруживает компилятор) заключается в том, чтобы забыть точку с запятой в для:

 for (a <- al;
      b <- bl
      if (a < b)) // ...

и где разместить выход:

 for (a <- al) yield {
     val x = foo (a).map (b).filter (c)
     if (x.cond ()) 9 else 14 
 }

вместо

 for (a <- al) {
     val x = foo (a).map (b).filter (c)
     if (x.cond ()) yield 9 else yield 14  // why don't ya yield!
 }

и забудьте знак равенства для метода:

 def yoyo (aka : Aka) : Zirp { // ups!
     aka.floskel ("foo")
 }

Ответ 6

Использование операторов if. Обычно вы можете реорганизовать код для использования if-выражений или с помощью фильтра.

Использование слишком большого количества vars вместо vals.

Вместо циклов, как говорили другие, используйте функции распознавания списка, такие как map, filter, foldLeft и т.д. Если нет ни одного доступного вам (смотрите внимательно, что-то, что вы можете использовать), используйте хвост рекурсии.

Вместо сеттеров я сохраняю дух функционального программирования и постоянно меняю объекты. Поэтому вместо этого я делаю что-то вроде этого, когда возвращаю новый объект:

class MyClass(val x: Int) {
    def setX(newx: Int) = new MyClass(newx)
}

Я стараюсь как можно больше работать со списками. Кроме того, для генерации списков вместо использования цикла используйте выражения for/yield.

Ответ 7

Использование массивов.

Это базовый материал, который легко распознается и фиксируется, но сначала замедляет вас, когда он кусает вашу задницу.

Scala имеет объект Array, в то время как в Java это встроенный артефакт. Это означает, что инициализация и доступ к элементам массива в Scala - это фактически вызовы методов:

//Java
//Initialise
String [] javaArr = {"a", "b"};
//Access
String blah1 = javaArr[1];  //blah1 contains "b"

//Scala
//Initialise
val scalaArr = Array("c", "d")  //Note this is a method call against the Array Singleton
//Access
val blah2 = scalaArr(1)  //blah2 contains "d"