Potrzebuję kilku próbek kodu (i też jestem ich bardzo ciekawy) kodu Scala i Java, które pokazują, że kod Scala jest prostszy i bardziej zwięzły niż kod napisany w Javie (oczywiście oba przykłady powinny rozwiązać ten sam problem).
Jeśli istnieje tylko próbka Scala z komentarzem typu „to jest fabryka abstrakcyjna w Scali, w Javie będzie to wyglądać znacznie bardziej uciążliwie”, to również jest to dopuszczalne.
Dzięki!
Najbardziej podobają mi się akceptowane i takie odpowiedzi
java
scala
comparison
language-features
rzymski
źródło
źródło
Odpowiedzi:
Poprawmy przykład układarki i używać Scala klas przypadków :
case class Person(firstName: String, lastName: String)
Powyższa klasa Scala zawiera wszystkie cechy poniższej klasy Java, a także kilka innych - na przykład obsługuje dopasowywanie wzorców (czego Java nie ma). Scala 2.8 dodaje nazwane i domyślne argumenty, które są używane do generowania metody kopiowania dla klas przypadków, co daje taką samą możliwość, jak metody with * w następującej klasie 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 + ")"; } }
Następnie w użyciu mamy (oczywiście):
Person mr = new Person("Bob", "Dobbelina"); Person miss = new Person("Roberta", "MacSweeney"); Person mrs = miss.withLastName(mr.getLastName());
Przeciwko
val mr = Person("Bob", "Dobbelina") val miss = Person("Roberta", "MacSweeney") val mrs = miss copy (lastName = mr.lastName)
źródło
productElements
aunapply
nie w konstruktorze, polu lub akcesorium: gist.github.com/424375case class Person(val firstName: String, val lastName: String)
co z tego? Uczynienie tego czymś prywatnym byłoby również możliwe, ale nie ma sensu z powodu braku zastosowania itp.case class Person(private val firstName: String)
, ale nie powinieneś wtedy używać klas przypadków. Zamiast tego zrób toclass Person(firstName: String)
ifirstName
domyślnie jest prywatne.val
iprivate val
polega na tym, że metody akcesorów, tj.firstName()
ifirstName(String)
, są publiczne czy prywatne. W Scali pola są zawsze prywatne. Aby Scala generowała metody get / set w stylu Java (oprócz metod dostępu w stylu Scala), istnieje@BeanProperty
adnotacja.Ten wydał mi się imponujący
Jawa
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)
Oprócz tych (przepraszam, że nie wklejam, nie chciałem kraść kodu)
źródło
getFirstName
igetLastName
metod. Abyscala.reflect.BeanProperty
to zrobić, musisz opisać parametry adnotacjami.get
. Idiomatyczny kod Java różni się od idiomatycznego kodu Scala. Czasamiis
przedrostek używany dla wartości logicznych. davetron5000.github.com/scala-style/naming_conventions/methods/…case class
i dostaćtoString
,equals
ihashCode
za darmo (i nie musisz teżval
jawnie przedstawiać argumentów ):case class Person(firstName: String, lastName: String)
case class
nie tylkoclass
.Zadanie: napisać program do indeksowania listy słów kluczowych (np. Książek).
Wyjaśnienie:
Jawa:
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); } }
Scala:
object Main extends App { val keywords = List("Apple", "Ananas", "Mango", "Banana", "Beer") val result = keywords.sorted.groupBy(_.head) println(result) }
źródło
Zadanie:
Masz listę
people
obiektów klasy,Person
która ma polaname
iage
. Twoim zadaniem jest posortowanie tej listy najpierw wedługname
, a następnie wedługage
.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()); } });
Scala:
val sortedPeople = people.sortBy(p => (p.name, p.age))
Aktualizacja
Odkąd napisałem tę odpowiedź, nastąpił spory postęp. Lambdy (i odwołania do metod) w końcu wylądowały w Javie i szturmem podbijają świat Javy.
Tak będzie wyglądał powyższy kod z Javą 8 (nadesłana przez @fredoverflow):
people.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
Chociaż ten kod jest prawie tak krótki, nie działa tak elegancko jak kod Scala.
W roztworze Scala The
Seq[A]#sortBy
sposób przyjmuje funkcjęA => B
, gdzieB
jest wymagane, aby miećOrdering
.Ordering
jest klasą typu. Pomyśl najlepiej o obu światach: tak jakComparable
, jest to niejawne dla danego typu, ale podobnieComparator
, jest rozszerzalne i może być dodane retrospektywnie do typów, które go nie miały. Ponieważ w Javie brakuje klas typów, musi ona powielać każdą taką metodę, raz naComparable
, potem naComparator
. Na przykład zobaczcomparing
ithenComparing
tutaj .Klasy typów pozwalają na pisanie reguł, takich jak „Jeśli A ma porządek, a B ma porządek, to ich krotka (A, B) również ma uporządkowanie”. W kodzie to znaczy:
implicit def pairOrdering[A : Ordering, B : Ordering]: Ordering[(A, B)] = // impl
W ten sposób
sortBy
w naszym kodzie można porównać nazwę, a następnie wiek. Ta semantyka zostanie zakodowana powyższą „regułą”. Programista Scala intuicyjnie spodziewałby się, że to zadziała w ten sposób. Żadnych specjalnych metod, takich jakcomparing
nie trzeba było dodawaćOrdering
.Lambdy i odwołania do metod to tylko wierzchołek góry lodowej, jaką jest programowanie funkcjonalne. :)
źródło
Zadanie:
Masz plik XML „company.xml”, który wygląda następująco:
<?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>
Musisz przeczytać ten plik i wydrukować pola
firstName
ilastName
wszystkich pracowników.Java: [pobrane stąd ]
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: [pobrane stąd , slajd nr 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) } } } } }
[EDYCJA przez Bill; Sprawdź komentarze do dyskusji] -
Hmm, jak to zrobić bez odpowiedzi w niesformatowanej sekcji odpowiedzi ... Hmph. Myślę, że zmienię twoją odpowiedź i pozwolę ci ją usunąć, jeśli ci to przeszkadza.
Oto jak zrobiłbym to w Javie z lepszymi bibliotekami:
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]); } }
To tylko szybki hack bez użycia magii i wszystkich elementów wielokrotnego użytku. Gdybym chciał dodać trochę magii, mógłbym zrobić coś lepszego niż zwrócenie tablicy tablic ciągów, ale nawet jeśli to jest, GoodXMLLib byłby całkowicie wielokrotnego użytku. Pierwszym parametrem scanFor jest sekcja, wszystkie przyszłe parametry będą elementami do znalezienia, które są ograniczone, ale interfejs mógłby zostać nieznacznie wzmocniony, aby dodać wiele poziomów dopasowania bez żadnego problemu.
Przyznam, że Java ma ogólnie dość słabe wsparcie dla bibliotek, ale daj spokój - porównanie okropnego użycia dziesięcioletniej (?) Biblioteki Java Java z implementacją opartą na zwięzłości jest po prostu niesprawiedliwe - i jest daleko z porównania języków!
źródło
Mapa działań do wykonania w zależności od ciągu.
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");
Scala:
val todos = Map( "hi" -> { () => println("Good morning!") } ) val defaultFun = () => println("task not found") todos.getOrElse("hi", defaultFun).apply()
A wszystko to w najlepszym możliwym guście!
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();
źródło
todos.get("hi")
zwroty,Option[()=>Unit]
które są potrzebne do prawidłowego dopasowania.val defaultFun = {() => println("task not found")}; todos.getOrElse("hi", defaultFun).apply()
val todos = Map("hi" -> { () => println("Good morning!") }) withDefaultValue { () => println("task not found") }
a potemtodos("hi")()
Piszę teraz grę w blackjacka w Scali. Oto jak wyglądałaby moja metoda dealerWins w Javie:
boolean dealerWins() { for(Player player : players) if (player.beats(dealer)) return false; return true; }
Oto jak to wygląda w Scali:
def dealerWins = !(players.exists(_.beats(dealer)))
Brawo dla funkcji wyższego rzędu!
Rozwiązanie Java 8:
boolean dealerWins() { return players.stream().noneMatch(player -> player.beats(dealer)); }
źródło
def dealerWins = !(players exists (_ beats dealer))
Podobał mi się ten prosty przykład sortowania i transformacji zaczerpnięty z książki „Beginning Scala” Davida Pollaka:
W Scali:
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)))
W Javie:
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)
źródło
A co z Quicksort?
Jawa
Poniżej znajduje się przykład Java znaleziony przez wyszukiwarkę Google,
adres URL to 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
Szybka próba wersji Scala. Otwarty sezon na ulepszacze kodu; @)
def qsort(l: List[Int]): List[Int] = { l match { case Nil => Nil case pivot::tail => qsort(tail.filter(_ < pivot)) ::: pivot :: qsort(tail.filter(_ >= pivot)) } }
źródło
partition
uniknąć.Lubiłem nieznanego użytkownika za odpowiedź tyle mam zamiar spróbować poprawić na nim. Poniższy kod nie jest bezpośrednim tłumaczeniem przykładu Java, ale wykonuje to samo zadanie przy użyciu tego samego interfejsu 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) }
źródło
Bardzo podoba mi się metoda getOrElseUpdate, znaleziona w mutableMap i pokazana tutaj, pierwsza Java, bez:
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; }
tak - WordCount, a tu w scali:
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 }
A oto w Javie 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; }
A jeśli chcesz być w 100% funkcjonalny:
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
isort
zostały już pokazane, ale zobacz, jak łatwo są zintegrowane z mapą: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) }
źródło
Oto bardzo prosty przykład: Kwadratowe liczby całkowite, a następnie je dodaj
public int sumSquare(int[] list) { int s = 0; for(int i = 0; i < list.length; i++) { s += list[i] * list[i]; } return s; }
W scali:
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)
Mapa kompaktowa stosuje funkcję do wszystkich elementów tablicy, więc:
Array(1,2,3).map(square) Array[Int] = Array(1, 4, 9)
Zwinięcie w lewo rozpocznie się od 0 jako akumulatora (akumulatorów) i zastosuje się
add(s,i)
do wszystkich elementów (i) tablicy, więc:Array(1,4,9).foldLeft(0)(add) // return 14 form 0 + 1 + 4 + 9
Teraz można to dalej zagęścić, aby:
Array(1,2,3).map(x => x * x ).foldLeft(0)((s,i) => s + i )
Tego nie spróbuję w Javie (za dużo pracy), zamień XML na mapę:
<a> <b id="a10">Scala</b> <b id="b20">rules</b> </a>
Kolejna linijka do pobrania mapy z 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)
źródło
sumSquare
w Scali polega na tym, że wygląda bardzo tajemniczo dla programisty Java, który da im amunicję przeciwko tobie, aby narzekać, że Scala jest niejasna i skomplikowana ...Problem: musisz zaprojektować metodę, która będzie wykonywała dowolny kod asynchronicznie.
Rozwiązanie w Javie :
/** * 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 } });
To samo w Scali (przy użyciu aktorów):
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 }
źródło
Wzorzec wyłącznika obwodu z wydania Michaela Nygarda w FaKods ( link do kodu )
implementacja wygląda tak w Scali:
. . . addCircuitBreaker("test", CircuitBreakerConfiguration(100,10)) . . . class Test extends UsingCircuitBreaker { def myMethodWorkingFine = { withCircuitBreaker("test") { . . . } } def myMethodDoingWrong = { withCircuitBreaker("test") { require(false,"FUBAR!!!") } } }
Co moim zdaniem jest super miłe. Wygląda jak część języka, ale jest prostym połączeniem w obiekcie CircuitBreaker wykonującym całą pracę.
/** * Basic MixIn for using CircuitBreaker Scope method * * @author Christopher Schmidt */ trait UsingCircuitBreaker { def withCircuitBreaker[T](name: String)(f: => T): T = { CircuitBreaker(name).invoke(f) } }
Odniesienie w innych językach Google dla „Wyłącznik” + Twój język.
źródło
Przygotowuję dokument zawierający kilka przykładów kodu Java i Scala, wykorzystując tylko proste do zrozumienia funkcje Scala:
Scala: Lepsza Java
Jeśli chciałbyś abym coś do niego dodała, proszę o odpowiedź w komentarzach.
źródło
Dobrym przykładem są leniwie oceniane nieskończone strumienie:
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 }
Oto pytanie dotyczące nieskończonych strumieni w Javie: Czy nieskończony iterator jest złym projektem?
Innym dobrym przykładem są funkcje i zamknięcia pierwszej klasy:
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 nie obsługuje funkcji pierwszej klasy, a naśladowanie domknięć za pomocą anonimowych klas wewnętrznych nie jest zbyt eleganckie. Inną rzeczą, której ten przykład nie może zrobić, jest uruchomienie kodu z interpretera / REPL. Uważam, że jest to niezwykle przydatne do szybkiego testowania fragmentów kodu.
źródło
Iterable
iIterator
tworzyć nieskończone strumienie.Dlaczego nikt tego wcześniej nie opublikował:
Jawa:
class Hello { public static void main( String [] args ) { System.out.println("Hello world"); } }
116 znaków.
Scala:
object Hello extends App { println("Hello world") }
56 znaków.
źródło
Application
cecha uważana za szkodliwą ... scala-blogs.org/2008/07/…Ten kod 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)) }) }
... byłoby całkowicie nieczytelne w Javie, jeśli to w ogóle możliwe.
źródło