Борьба с привычками, сложившимися в Java при переходе на Scala - PullRequest
13 голосов
/ 03 марта 2011

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

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

РЕДАКТИРОВАТЬ: еще один использует собственные методы получения / установки вместо методов, любезно сгенерированных Scala

Ответы [ 7 ]

9 голосов
/ 03 марта 2011

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

8 голосов
/ 03 марта 2011

Один очевидный из них - не использовать преимущества вложенной области видимости, которую допускает 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"));
}

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

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")
}

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

7 голосов
/ 03 марта 2011

Пара моих любимых:

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

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

3 голосов
/ 03 марта 2011

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

(*) В Option: есть отличный шпаргалка для сопоставления с образцом: http://blog.tmorris.net/scalaoption-cheat-sheet/

1 голос
/ 23 марта 2011

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

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

В 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"
1 голос
/ 22 марта 2011

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

Использование слишком большого числа переменных вместо значений.

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

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

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

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

1 голос
/ 22 марта 2011

До сих пор я не принимал ленивых валов и ручьев.

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

 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")
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...