Мне нужны образцы кода (и мне они очень интересны) кода Scala и Java, которые показывают, что код Scala более простой и лаконичный, чем код, написанный на Java (конечно, оба образца должны решать одну и ту же проблему).
Если есть только образец Scala с комментарием типа «это абстрактная фабрика в Scala, в Java она будет выглядеть намного громоздче», то это также приемлемо.
Благодарность!
Мне больше всего нравится принятое и это ответы
java
scala
comparison
language-features
Римский
источник
источник
Ответы:
Давайте улучшим пример стека и воспользуемся case-классами Scala :
case class Person(firstName: String, lastName: String)
Вышеупомянутый класс Scala содержит все функции указанного ниже класса Java и некоторые другие - например, он поддерживает сопоставление с образцом (чего нет в Java). Scala 2.8 добавляет именованные аргументы и аргументы по умолчанию, которые используются для создания метода копирования для классов case, что дает те же возможности, что и методы with * следующего класса Java.
public class Person implements Serializable { private final String firstName; private final String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public Person withFirstName(String firstName) { return new Person(firstName, lastName); } public Person withLastName(String lastName) { return new Person(firstName, lastName); } public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Person person = (Person) o; if (firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) { return false; } if (lastName != null ? !lastName.equals(person.lastName) : person.lastName != null) { return false; } return true; } public int hashCode() { int result = firstName != null ? firstName.hashCode() : 0; result = 31 * result + (lastName != null ? lastName.hashCode() : 0); return result; } public String toString() { return "Person(" + firstName + "," + lastName + ")"; } }
Тогда в использовании у нас есть (конечно):
Person mr = new Person("Bob", "Dobbelina"); Person miss = new Person("Roberta", "MacSweeney"); Person mrs = miss.withLastName(mr.getLastName());
Против
val mr = Person("Bob", "Dobbelina") val miss = Person("Roberta", "MacSweeney") val mrs = miss copy (lastName = mr.lastName)
источник
productElements
иunapply
, не в конструкторе, поле, или аксессора: gist.github.com/424375case class Person(val firstName: String, val lastName: String)
Ну и что? Сделать эту вещь приватной тоже можно, но это не имеет никакого смысла из-за неприменения и т. Д.case class Person(private val firstName: String)
, но тогда вам не следует использовать case-классы. Вместо этого doclass Person(firstName: String)
иfirstName
по умолчанию закрыто.val
иprivate val
заключается в том, являются ли методы доступа, т. е.firstName()
иfirstName(String)
, общедоступными или частными. В Scala поля всегда приватны. Чтобы Scala могла генерировать методы get / set в стиле Java (в дополнение к средствам доступа в стиле Scala), существует@BeanProperty
аннотация.Я нашел это впечатляющим
Ява
public class Person { private final String firstName; private final String lastName; public Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } }
Scala
class Person(val firstName: String, val lastName: String)
А также эти (извините, что не вставил, я не хотел воровать код)
источник
getFirstName
иgetLastName
методы. Для этого вам необходимо аннотировать параметрыscala.reflect.BeanProperty
аннотациями.get
. Идиоматический код Java отличается от идиоматического кода Scala. Иногдаis
префикс используется для логических значений. davetron5000.github.com/scala-style/naming_conventions/methods/…case class
и получитьtoString
,equals
причемhashCode
бесплатно (и вам также не нужноval
явно приводить аргументы ):case class Person(firstName: String, lastName: String)
case class
, а не для простоclass
.Задача: написать программу для индексации списка ключевых слов (например, книг).
Пояснение:
Ява:
import java.util.*; class Main { public static void main(String[] args) { List<String> keywords = Arrays.asList("Apple", "Ananas", "Mango", "Banana", "Beer"); Map<Character, List<String>> result = new HashMap<Character, List<String>>(); for(String k : keywords) { char firstChar = k.charAt(0); if(!result.containsKey(firstChar)) { result.put(firstChar, new ArrayList<String>()); } result.get(firstChar).add(k); } for(List<String> list : result.values()) { Collections.sort(list); } System.out.println(result); } }
Скала:
object Main extends App { val keywords = List("Apple", "Ananas", "Mango", "Banana", "Beer") val result = keywords.sorted.groupBy(_.head) println(result) }
источник
Задача:
У вас есть список
people
объектов класса, вPerson
котором есть поляname
иage
. Ваша задача отсортировать этот список сначала поname
, а затем поage
.Java 7:
Collections.sort(people, new Comparator<Person>() { public int compare(Person a, Person b) { return a.getName().compare(b.getName()); } }); Collections.sort(people, new Comparator<Person>() { public int compare(Person a, Person b) { return Integer.valueOf(a.getAge()).compare(b.getAge()); } });
Скала:
val sortedPeople = people.sortBy(p => (p.name, p.age))
Обновить
С тех пор, как я написал этот ответ, был достигнут некоторый прогресс. Лямбды (и ссылки на методы) наконец-то попали в Java, и они покоряют мир Java.
Вот как будет выглядеть приведенный выше код с Java 8 (предоставлено @fredoverflow):
people.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Хотя этот код почти такой же короткий, он работает не так элегантно, как код Scala.
В растворе Scala, то
Seq[A]#sortBy
метод принимает функцию ,A => B
гдеB
требуется , чтобы иметьOrdering
.Ordering
это тип-класс. Лучше подумайте об обоих мирах: напримерComparable
, это неявно для рассматриваемого типа, но, напримерComparator
, оно расширяемо и может быть ретроспективно добавлено к типам, у которых его не было. Поскольку в Java отсутствуют классы типов, она должна дублировать каждый такой метод один раз дляComparable
, а затем дляComparator
. Например, см.comparing
ИthenComparing
здесь .Классы типов позволяют писать такие правила, как «Если A имеет порядок, а B имеет порядок, то их кортеж (A, B) также имеет порядок». В коде это:
implicit def pairOrdering[A : Ordering, B : Ordering]: Ordering[(A, B)] = // impl
Вот как
sortBy
в нашем коде можно сравнивать по имени, а затем по возрасту. Эта семантика будет закодирована с помощью вышеуказанного «правила». Программист на Scala интуитивно ожидал, что это будет работать именно так. Никаких специальных методов вродеcomparing
не нужно было добавлятьOrdering
.Лямбда-выражения и ссылки на методы - это лишь верхушка айсберга функционального программирования. :)
источник
Задача:
У вас есть XML-файл company.xml, который выглядит так:
<?xml version="1.0"?> <company> <employee> <firstname>Tom</firstname> <lastname>Cruise</lastname> </employee> <employee> <firstname>Paul</firstname> <lastname>Enderson</lastname> </employee> <employee> <firstname>George</firstname> <lastname>Bush</lastname> </employee> </company>
Вы должны прочитать этот файл и распечатать поля
firstName
иlastName
всех сотрудников.Java: [взято отсюда ]
import java.io.File; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class XmlReader { public static void main(String[] args) { try { File file = new File("company.xml"); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(file); doc.getDocumentElement().normalize(); NodeList nodeLst = doc.getElementsByTagName("employee"); for (int s = 0; s < nodeLst.getLength(); s++) { Node fstNode = nodeLst.item(s); if (fstNode.getNodeType() == Node.ELEMENT_NODE) { Element fstElmnt = (Element) fstNode; NodeList fstNmElmntLst = fstElmnt.getElementsByTagName("firstname"); Element fstNmElmnt = (Element) fstNmElmntLst.item(0); NodeList fstNm = fstNmElmnt.getChildNodes(); System.out.println("First Name: " + ((Node) fstNm.item(0)).getNodeValue()); NodeList lstNmElmntLst = fstElmnt.getElementsByTagName("lastname"); Element lstNmElmnt = (Element) lstNmElmntLst.item(0); NodeList lstNm = lstNmElmnt.getChildNodes(); System.out.println("Last Name: " + ((Node) lstNm.item(0)).getNodeValue()); } } } catch (Exception e) { e.printStackTrace(); } } }
Scala: [взято отсюда , слайд №19]
import xml.XML object XmlReader { def main(args: Array[String]): Unit = { XML.loadFile("company.xml") match { case <employee> { employees @ _* } </employee> => { for(e <- employees) { println("First Name: " + (e \ "firstname").text) println("Last Name: " + (e \ "lastname").text) } } } } }
[РЕДАКТИРОВАТЬ Билл; Проверить комментарии для обсуждения] -
Хм, как это сделать, не отвечая в неформатированном разделе ответов ... Хм. Думаю, я отредактирую ваш ответ и позволю вам удалить его, если он вас беспокоит.
Вот как я бы сделал это на Java с лучшими библиотеками:
public scanForEmployees(String filename) { GoodXMLLib source=new GoodXMLLib(filename); while( String[] employee: source.scanFor("employee", "firstname", "lastname") ) { System.out.println("First Name: " + employee[0]); System.out.println("Last Name: " + employee[1]); } }
Это всего лишь быстрый взлом без магии и со всеми компонентами многократного использования. Если бы я хотел добавить немного волшебства, я мог бы сделать что-то лучше, чем возвращать массив строковых массивов, но даже в этом случае GoodXMLLib можно было бы полностью использовать повторно. Первый параметр scanFor - это раздел, все будущие параметры будут ограниченными элементами для поиска, но интерфейс можно немного улучшить, чтобы добавить несколько уровней сопоставления без каких-либо реальных проблем.
Я признаю, что в Java есть довольно плохая поддержка библиотек в целом, но давай - сравнивать ужасное использование десятилетней (?) Старой XML-библиотеки Java с реализацией, основанной на краткости, просто нечестно - и далеко из сравнения языков!
источник
Карта действий для выполнения в зависимости от строки.
Java 7:
// strategy pattern = syntactic cruft resulting from lack of closures public interface Todo { public void perform(); } final Map<String, Todo> todos = new HashMap<String,Todo>(); todos.put("hi", new Todo() { public void perform() { System.out.println("Good morning!"); } } ); final Todo todo = todos.get("hi"); if (todo != null) todo.perform(); else System.out.println("task not found");
Скала:
val todos = Map( "hi" -> { () => println("Good morning!") } ) val defaultFun = () => println("task not found") todos.getOrElse("hi", defaultFun).apply()
И все это сделано в лучшем вкусе!
Java 8:
Map<String, Runnable> todos = new HashMap<>(); todos.put("hi", () -> System.out.println("Good morning!")); Runnable defaultFun = () -> System.out.println("task not found"); todos.getOrDefault("hi", defaultFun).run();
источник
todos.get("hi")
возвращает,Option[()=>Unit]
который необходим для правильного соответствия.val defaultFun = {() => println("task not found")}; todos.getOrElse("hi", defaultFun).apply()
val todos = Map("hi" -> { () => println("Good morning!") }) withDefaultValue { () => println("task not found") }
а потомtodos("hi")()
Мне понравился этот простой пример сортировки и преобразования, взятый из книги Дэвида Поллака Beginning Scala:
В Scala:
def validByAge(in: List[Person]) = in.filter(_.valid).sortBy(_.age).map(_.first) case class Person(val first: String, val last: String, val age: Int) {def valid: Boolean = age > 18} validByAge(List(Person("John", "Valid", 32), Person("John", "Invalid", 17), Person("OtherJohn", "Valid", 19)))
В Java:
public static List<String> validByAge(List<Person> in) { List<Person> people = new ArrayList<Person>(); for (Person p: in) { if (p.valid()) people.add(p); } Collections.sort(people, new Comparator<Person>() { public int compare(Person a, Person b) { return a.age() - b.age(); } } ); List<String> ret = new ArrayList<String>(); for (Person p: people) { ret.add(p.first); } return ret; } public class Person { private final String firstName; private final String lastName; private final Integer age; public Person(String firstName, String lastName, Integer age) { this.firstName = firstName; this.lastName = lastName; this.age = age; } public String getFirst() { return firstName; } public String getLast() { return lastName; } public Integer getAge() { return age; } public Boolean valid() { return age > 18; } } List<Person> input = new ArrayList<Person>(); input.add(new Person("John", "Valid", 32)); input.add(new Person("John", "InValid", 17)); input.add(new Person("OtherJohn", "Valid", 19)); List<Person> output = validByAge(input)
источник
Я сейчас пишу игру в блэкджек на Scala. Вот как будет выглядеть мой метод дилераWins на Java:
boolean dealerWins() { for(Player player : players) if (player.beats(dealer)) return false; return true; }
Вот как это выглядит в Scala:
def dealerWins = !(players.exists(_.beats(dealer)))
Ура функциям высшего порядка!
Решение Java 8:
boolean dealerWins() { return players.stream().noneMatch(player -> player.beats(dealer)); }
источник
def dealerWins = !(players exists (_ beats dealer))
Как насчет быстрой сортировки?
Ява
Ниже приведен пример Java, найденный с помощью поиска в Google.
URL-адрес http://www.mycstutorials.com/articles/sorting/quicksort
public void quickSort(int array[]) // pre: array is full, all elements are non-null integers // post: the array is sorted in ascending order { quickSort(array, 0, array.length - 1); // quicksort all the elements in the array } public void quickSort(int array[], int start, int end) { int i = start; // index of left-to-right scan int k = end; // index of right-to-left scan if (end - start >= 1) // check that there are at least two elements to sort { int pivot = array[start]; // set the pivot as the first element in the partition while (k > i) // while the scan indices from left and right have not met, { while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first i++; // element greater than the pivot while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first k--; // element not greater than the pivot if (k > i) // if the left seekindex is still smaller than swap(array, i, k); // the right index, swap the corresponding elements } swap(array, start, k); // after the indices have crossed, swap the last element in // the left partition with the pivot quickSort(array, start, k - 1); // quicksort the left partition quickSort(array, k + 1, end); // quicksort the right partition } else // if there is only one element in the partition, do not do any sorting { return; // the array is sorted, so exit } } public void swap(int array[], int index1, int index2) // pre: array is full and index1, index2 < array.length // post: the values at indices 1 and 2 have been swapped { int temp = array[index1]; // store the first value in a temp array[index1] = array[index2]; // copy the value of the second into the first array[index2] = temp; // copy the value of the temp into the second }
Scala
Быстрая попытка создания версии на Scala. Открытый сезон для улучшителей кода; @)
def qsort(l: List[Int]): List[Int] = { l match { case Nil => Nil case pivot::tail => qsort(tail.filter(_ < pivot)) ::: pivot :: qsort(tail.filter(_ >= pivot)) } }
источник
partition
избежать.Мне настолько понравился ответ пользователя unknown, что я постараюсь его улучшить. Приведенный ниже код не является прямым переводом примера Java, но он выполняет ту же задачу с тем же API.
def wordCount (sc: Scanner, delimiter: String) = { val it = new Iterator[String] { def next = sc.nextLine() def hasNext = sc.hasNextLine() } val words = it flatMap (_ split delimiter iterator) words.toTraversable groupBy identity mapValues (_.size) }
источник
Мне очень нравится метод getOrElseUpdate, найденный в mutableMap и показанный здесь, сначала Java, без:
public static Map <String, Integer> wordCount (Scanner sc, String delimiters) { Map <String, Integer> dict = new HashMap <String, Integer> (); while (sc.hasNextLine ()) { String[] words = sc.nextLine ().split (delimiters); for (String word: words) { if (dict.containsKey (word)) { int count = dict.get (word); dict.put (word, count + 1); } else dict.put (word, 1); } } return dict; }
да - WordCount, а здесь в scala:
def wordCount (sc: Scanner, delimiter: String) = { val dict = new scala.collection.mutable.HashMap [String, Int]() while (sc.hasNextLine ()) { val words = sc.nextLine.split (delimiter) words.foreach (word => dict.update (word, dict.getOrElseUpdate (word, 0) + 1)) } dict }
А вот и в Java 8:
public static Map<String, Integer> wordCount(Scanner sc, String delimiters) { Map<String, Integer> dict = new HashMap<>(); while (sc.hasNextLine()) { String[] words = sc.nextLine().split(delimiters); Stream.of(words).forEach(word -> dict.merge(word, 1, Integer::sum)); } return dict; }
А если вы хотите работать на 100%:
import static java.util.function.Function.identity; import static java.util.stream.Collectors.*; public static Map<String, Long> wordCount(Scanner sc, String delimiters) { Stream<String> stream = stream(sc.useDelimiter(delimiters)); return stream.collect(groupingBy(identity(), counting())); } public static <T> Stream<T> stream(Iterator<T> iterator) { Spliterator<T> spliterator = Spliterators.spliteratorUnknownSize(iterator, 0); return StreamSupport.stream(spliterator, false); }
filter
иsort
уже были показаны, но посмотрите, как легко они интегрируются с картой:def filterKeywords (sc: Scanner, keywords: List[String]) = { val dict = wordCount (sc, "[^A-Za-z]") dict.filter (e => keywords.contains (e._1)).toList . sort (_._2 < _._2) }
источник
Это очень простой пример: возведите целые числа в квадрат и затем добавьте их.
public int sumSquare(int[] list) { int s = 0; for(int i = 0; i < list.length; i++) { s += list[i] * list[i]; } return s; }
В scala:
val ar = Array(1,2,3) def square(x:Int) = x * x def add(s:Int,i:Int) = s+i ar.map(square).foldLeft(0)(add)
Компактная карта применяет функцию ко всем элементам массива, поэтому:
Array(1,2,3).map(square) Array[Int] = Array(1, 4, 9)
Сгиб влево будет начинаться с 0 в качестве аккумулятора (ов) и применяться
add(s,i)
ко всем элементам (i) массива, так что:Array(1,4,9).foldLeft(0)(add) // return 14 form 0 + 1 + 4 + 9
Теперь это можно дополнительно уплотнить до:
Array(1,2,3).map(x => x * x ).foldLeft(0)((s,i) => s + i )
Это я не буду пробовать на Java (много работы), превратите XML в карту:
<a> <b id="a10">Scala</b> <b id="b20">rules</b> </a>
Еще один лайнер для получения карты из XML:
val xml = <a><b id="a10">Scala</b><b id="b20">rules</b></a> val map = xml.child.map( n => (n \ "@id").text -> n.child.text).toMap // Just to dump it. for( (k,v) <- map) println(k + " --> " + v)
источник
sumSquare
в Scala заключается в том, что Java-разработчику это кажется очень загадочным, и это даст им повод против вас жаловаться на то, что Scala неясен и сложен ...Проблема: вам нужно разработать метод, который будет выполнять любой заданный код асинхронно.
Решение на Java :
/** * This method fires runnables asynchronously */ void execAsync(Runnable runnable){ Executor executor = new Executor() { public void execute(Runnable r) { new Thread(r).start(); } }; executor.execute(runnable); } ... execAsync(new Runnable() { public void run() { ... // put here the code, that need to be executed asynchronously } });
То же самое в Scala (с использованием актеров):
def execAsync(body: => Unit): Unit = { case object ExecAsync actor { start; this ! ExecAsync loop { react { case ExecAsync => body; stop } } } } ... execAsync{ // expressive syntax - don't need to create anonymous classes ... // put here the code, that need to be executed asynchronously }
источник
Паттерн "Автоматический выключатель" из альбома Майкла Найгарда "Release It in FaKods" ( ссылка на код )
реализация на Scala выглядит так:
. . . addCircuitBreaker("test", CircuitBreakerConfiguration(100,10)) . . . class Test extends UsingCircuitBreaker { def myMethodWorkingFine = { withCircuitBreaker("test") { . . . } } def myMethodDoingWrong = { withCircuitBreaker("test") { require(false,"FUBAR!!!") } } }
Что я считаю очень приятным. Он выглядит как часть языка, но это простая примесь в объекте CircuitBreaker, выполняющая всю работу.
/** * Basic MixIn for using CircuitBreaker Scope method * * @author Christopher Schmidt */ trait UsingCircuitBreaker { def withCircuitBreaker[T](name: String)(f: => T): T = { CircuitBreaker(name).invoke(f) } }
Ссылка на других языках Google для "Автоматический выключатель" + ваш язык.
источник
Я готовлю документ, который дает несколько примеров кода Java и Scala, используя только простые для понимания функции Scala:
Scala: лучшая Java
Если вы хотите, чтобы я что-то добавил, ответьте в комментариях.
источник
Бесконечные потоки с ленивой оценкой - хороший пример:
object Main extends Application { def from(n: Int): Stream[Int] = Stream.cons(n, from(n + 1)) def sieve(s: Stream[Int]): Stream[Int] = Stream.cons(s.head, sieve(s.tail filter { _ % s.head != 0 })) def primes = sieve(from(2)) primes take 10 print }
Вот вопрос, касающийся бесконечных потоков в Java: плохой ли дизайн бесконечного итератора?
Еще один хороший пример - функции и замыкания первого класса:
scala> def f1(w:Double) = (d:Double) => math.sin(d) * w f1: (w: Double)(Double) => Double scala> def f2(w:Double, q:Double) = (d:Double) => d * q * w f2: (w: Double,q: Double)(Double) => Double scala> val l = List(f1(3.0), f2(4.0, 0.5)) l: List[(Double) => Double] = List(<function1>, <function1>) scala> l.map(_(2)) res0: List[Double] = List(2.727892280477045, 4.0)
Java не поддерживает функции первого класса, и имитация замыканий с помощью анонимных внутренних классов не очень элегантна. Еще одна вещь, которую этот пример показывает, что Java не может сделать, - это запускать код из интерпретатора / REPL. Я считаю это чрезвычайно полезным для быстрого тестирования фрагментов кода.
источник
Iterable
иIterator
создавать бесконечные потоки.Почему никто не публиковал это раньше:
Ява:
class Hello { public static void main( String [] args ) { System.out.println("Hello world"); } }
116 знаков.
Скала:
object Hello extends App { println("Hello world") }
56 символов.
источник
Application
характеристика считается вредной ... scala-blogs.org/2008/07/…Этот код Scala ...
def partition[T](items: List[T], p: (T, T) => Boolean): List[List[T]] = { items.foldRight[List[List[T]]](Nil)((item: T, items: List[List[T]]) => items match { case (first :: rest) :: last if p (first, item) => (List(item)) :: (first :: rest) :: last case (first :: rest) :: last => (item :: first :: rest) :: last case _ => List(List(item)) }) }
... было бы совершенно нечитаемым на Java, если это вообще возможно.
источник