Java 8 / Scala - різніця в підходах та взаємні інновації

З великим задоволення Згадую JDays Lviv 2014. Я там розказувать про відношення между Scala та Java 8. З подивуватися помітів , что кількість ретвітів Презентації більша за звічайна для такого роду контенту , тому віділів годину , щоб записатися ее конспект , заодно скорістаюся Нагода та презентую декілька освітніх ініціатів .

Ми будемо Говорити про різніцю между Scala та Java , про місце ціх мов в екосістемі і чому Кожна з них по - своєму необхідна . Потік інновацій двонаправленній - є запозичення як Із Scala в Java , так и навпаки . Відповідь на питання Загальна - чі треба вчитува Scala если є Java , и навпаки однозначна: чім больше Суттєво різніх мов та стилів програмування ві добре там , тім ві кращий спеціаліст . ЯКЩО Запитати Scala - програміста , чім же принципова відрізняється Scala від Java , то ВІН з великою ймовірністю не якщо розказуваті про нюанси лямбда - функцій та трейтів , а просто наведе такий приклад : Java: Scala : Тобто одному рядку на Scala відповідає 20 на Java. З Іншого боку , відсутність лаконічності - Це проблема не Тільки Java як мови , альо ї культури , яка склалось в середовіщі Java - розробніків , Аджея можна Написати и так : У DTO - base hashMap та equals перевізначені помощью рефлексії . І ті , что я можу Відкрито Написати поле без геттерів та сеттерів во время конференции , и мене відразу НЕ закідають КАПЦОВ , - Досягнення того , что розвиток ідіоматічної Java потихенько відбувається в цьом Напрямки , Аджея Scala показала перспектівність лаконічності . У Java 8 додано ряд нововведень , что мают сделать ЗРУЧНИЙ функціональний стиль програмування , что на перший погляд повторюються відповідні конструкції Scala . В Першу Черга це:
- lambda - вирази ( анонімні Функції ) ;
- default methods in interfaces ( a - la traits in scala ) ;
- Потокові Операції над коллекціямі . Давайте розлянемо їх детальніше . Лямбда- вирази Java Scala бачим , что код Дуже схожий . Альо : Scala : Java: [ ?] ( модіфікувіті контекст , з которого БУВ вікліканій lambda - вирази , Неможливо ) . Тобто лямбда - вирази в Java - Це синтаксичний цукор над анонімнімі класами , что мают доступ Ліше до фінальніх об'єктів контексту , а в Scala - повноцінні замикання ( closure ) , что мают повний доступ до контексту . Дефолтні методи в інтерфейсах Інша фіча , яка такоже булу запозичили в Java Із Scala - Це дефолтні методи в інтерфейсах , что пріблізно відповідають трейтам в Scala . Java: Scala : На перший погляд - однаково . Альо : Java: [ ?] ( с помощью самє Java Такої функціональності не добиті , Деяк аналогом может буті аспектность ПІДХІД ) . Ще один приклад (можливо , Менш Важлива ): Java: [ ?] ( перегрузіті методи об'єктів в інтерфейсі Неможливо ) . Тобто бачим , что конструкції trait та default методів такоже й достатньо Різні : в Java Це Специфікація діспетчерізації виклику , а в Scala trait - Це більш загальна конструкція , что спеціфує побудову віхідного класу помощью процесу лінеарізації . Потокові Операції над коллекціямі І Третє нововведення Java - 8 - Це stream інтерфейс до бібліотеки колекцій , что по дизайну Дуже нагадує стандартну бібліотеку Scala . Java: Scala : Дуже схоже , Тільки в Java stream інтерфейс треба спочатку отріматі з коллекції , а потім - перевести в інтерфейс результату . Основна причина для цього - Сталість інтерфейсів . Це означає , что если в Java Вже є й достатньо комплектне нефункціональне API коллекцій , то додаваті до нього ще один функціональний інтерфейс НЕ є Доречний з точки зору дизайну API та легкості модіфікації , вікорістовування та засвоювання . Тобто це - ціна за поступовій еволюційній розвиток . Добре , Спробуємо порівняті далі : Java: persons.parallelStream ( ) . filter ( x - & gt ; x.person == " Jon " ) . collect ( Collectors.toList ( )) Scala : persons.par.filter ( _ . person == " Jon " ) Тут решение Дуже Схожі , в Java можна сделать « паралельний » stream , в Scala - паралельних коллекцію . Доступ до баз SQL : Scala : db.persons.filter ( _ . firstName === " Jon " ) . toList У Java - екосістемі все ж таки є аналог . Там можна Написати : dbStream ( em , Person.class ) .filter ( x - & gt ; x.firstName.equals ( " Jon " )) . toList Цікаво порівняті , як самє Це відображення колекцій в табліці баз данних реалізовано в обох випадка . У Scala - варіанті Операції мают тіпі операцій над Даними . Если пріблізно описати тіпі : persons має тип TableQuery [ PersonTable ] де PersonTable & lt ;: Table [ Person ] , что має структуру , у Якої є методи firstName та lastName . firstName === lastName - Це бінарна Операція === (так , у Scala можна візначаті свои інфіксні Операції ) , что має тип , подібний до Column [ X ] * Column [ Y ] = & gt ; SqlExpr [ Boolean ] , а filtter SqlExpr [ Boolean ] Query [ T] має метод filter : SqlExpr [ Boolean ] = & gt ; Query [ T] та Якийсь метод для генерації SQL , І, таким чином , мі Можемо віразіті Щось як вирази над Table [ Person ] , что є відображенням Person . Це й достатньо просто и зрозуміло . Даже можна Сказати - трівіально . Тепер давайте подивимось , як цею самий функціонал реалізован у jinq : dbStream ( em , Person.class ) .filter ( x - & gt ; x.firstName.equals ( " Jon " )) . toList Тут тип x - самє Person , а x.firstName - String , метод filter пріймає на вхід функцію Person - & gt ; Boolean . Як же з неї генерується SQL ? Filter Аналізує байт - код (там побудовали Щось типу інтерпретатора байт -коду , Який ' символічно ' Виконує інструкції , а результатом цього Виконання є траса візового геттерів та функцій , за Якою можна побудуваті SQL) . Взагалі можна Знято Капелюха перед такою задумкою . З Іншої точки зору - все Це робиться дінамічно и в рантаймі ( тому - й достатньо Довгий) , и если ми вікорістуємо в нашому filter функцію НЕ з фіксованого переліку ( для Якої ми НЕ знаємо , як побудуваті SQL) , то відкріємо Це теж Тільки в рантаймі . Ну і бачим , что для cхожої функціональності код на Scala більш - Менш трівіальній , в тій годину як на Java Використовують складні технології на Грані фантастики . Це були запозичення Із Scala в Java , альо , як бачим , - Java -версії « фіч » сильно відрізняються від Scala . запозичення з Java8 в Scala Тепер подивимось на запозичення з Java8 в Scala . Процес інновацій двонаправленій , и є Одне нововведення Java8 , запозичення в Scala . В 2.11 воно включається опцією компілятора , а в 2.12 буде за замовчуванням . Це SAM - конверсія . Давайте вновь подивимось на два фрагменти коду : Java: Scala : Як бачим , в Java -версії тіпі и Параметри методів - Це Acceptor та Generator , что на Рівні байт -коду представляються як відповідні класи , а в Scala - Функції T = & gt ; Unit , та Unit = & gt ; T , что на Рівні байт -коду представляються як Function1.class SAM - type ( Single Abstract Method ) - клас або інтерфейс , у якому є один абстрактний метод . У Java , если метод пріймає як параметр SAM - type , можна податі функцію . В Scala до 2.11 - не так , функція - Це субкласів Function [ A , B ] . На перший погляд , Це не Дуже значні Зміни , крім того что можна буде об'єктно опісуваті функціональні API , альо на практіці у цієї фічі є Дуже Важлива прікладення - ! застосування SAM - інтерфейсів у Частинами , критичних до першої години . Чому ? Еффектівність віконування байт -коду інтерпретатором JIT покладів від того , чи мож ВІН провести агресивний інлайнінг . Альо если ві працюєте з функціональнімі інтерфейсамі , то класи параметрів віглядають як Function1 для будь - Якої Функції з одним параметром Function2 для всіх функцій з 2 - ма параметрами , и так далі . Звичайно , їх сильно не проінлайніш . Тому була така неявно проблема: у критичних по годині нізькорівневіх Частинами коду функціональні інтерфейси краще НЕ використовуват , ТОМУ ЩО JIT Не зможу їх проінлайніті . З SAM можна переписати їх через локальні SAM - тіпі, Які компілятор может проінлайніті , и ця проблема знікне . Правда , Вже існуючій код доведе змінюваті , а деякі РЕЧІ ( як, Наприклад , інтерфейси колекцій ) можна Було бі переписати через SAM , альо скомбінуваті новий та старий інтерфейси так , щоб усьо виглядать сумісно . Таку саму проблему ми бачили у Java , колі розглядалі інтерфейси колекцій . Це дозволяє Побачити , як працює еволюція. Покращена Java в одному Напрямки - неідеально , альо краще , чем Було. Покращена Scala в Іншому Напрямки - теж неідеально . І тепер у нас є Дві « крівості » у двох мовах , что віклікані повільною адаптацією . І є місце для третьої мови , яка может сделать « ідеальний » інтерфейс на Якийсь Наступний проміжок годині. Так еволюція и іде . Взагалі конструкції Scala , якіх немає в Java , можна розділіті на 2 класи :
- ті , Які в ідеальному мире колись будуть внесені в Java - 9,10,11,12 .. . ( если ці релізи будут існуваті і Java ще буде комусь цікава ) - така логіка розвітку , так само як Fortan - 90 ставши об'єктно - оріентованім ;
- ті, що показують самє різніцю в ідеології Java та Scala . До Першої групи можна Віднести case - класи та автоматічній вивід тіпів , а до Другої - почти все інше . Пам'ятаєте , на самому качану ми начали з Наступний фрагменту коду : case class Person ( firstName : String , lastName : String) Чому case - класи назіваються case ? Тому що їх можна використовуват в match/case операторі : Перший case спрацьовує на имя Jon Galt , другий - на будь -які Інші Значення Person . При чому , в области Дії іншого case вводяться два локальних имени - firstName та lastName Взагалі Це назівається ML - style pattern matching . ML - style - тому ще Вперше така конструкція ( матчінг , в якому заповняються Зміни ) булу запропонована в мові ML , что вінікла в 1973 году . Поза більшість «нових » мов ( Scala , Kotlin , Ceylon , Apple Swift ) ее підтрімують . Особливості Scala Спробуємо поставити; питання Загальна : а в чому самє особлівість Scala ? Які возможности вона Дає , якіх принципова немає в Java ? Відповідь - побудова внутренних DSL [ Domain Specific Language ] . Тобто Scala прістосована для того , щоб для кожної предметної области можна Було побудуваті Жорсткий тіпізовану модель та віразіті ее у мовних конструкціях . Ці конструкції будують в статично - тіпізованому середовіщі . Які основні Властивості дають нам можлівість будуваті Такі конструкції ?
- Гнучкий синтакс , синтаксичний цукор ,
- синтаксис передачі параметрів по имени ,
- макроси . почнемо з гнучкості синтаксису . Що це означає на практіці ? 1 . Методи могут назіватіся як завгодно : def +++ ( x : Int , y : Int ) = x * x * y * y 2 . Будь -який метод з одним параметром можна визвати як інфіксній : 1 to 100 == 1.to ( 100 ) 3 . Фігурні та квадратні дужки відрізняються Тільки тім , что в фігурніх дужках может буті кілька виразів . Один параметр можна передавіті и в фігурніх дужках : future ( 1 ) та future { 1 } 4 . Функції можна візначаті з декількома списками аргументів : def until ( cond : = & gt ; Boolean ) ( body : = & gt ; Unit) : Unit 5 . Як параметр Функції можна Передат блок коду , Що буде віклікаті КОЖЕН раз , коли відповідній аргумент буде назіватісь (передача аргументів «за ім'ям » ): def until ( cond : = & gt ; Boolean ) ( body : = & gt ; Unit) : Unit =   { Body ; while (! cond ) { body } } until ( x == 10 ) ( x + = 1 ) Давайте Спробуємо сделать для DSL для Do/until : Тепер ми Можемо Написати Щось в стілі Do {  x + = 1 } Until ( x ! = 10 ) Ще одна властівість , что дозволяє створюваті DSL , - Це Спеціальний синтаксис для Деяк віділеніх функцій . Скажімо , Наступний виразі : for ( x & lt ; - collection ) { doSomething } . Це просто синтаксис для виклику методу : collection.foreach ( x = & gt ; doSomething ) Отже если ми напішемо свой ??клас , у якому буде метод foreach , что пріймає на вхід Пєвнєв функцію з чогось в Unit , ( [ X ] = & gt ; Unit) то далі в коді ми зможемо використовуват синтаксис for для свого типу. Ті самє з конструкцією for/yield ( для map ) , вкладень ітераціямі ( flatMap ) та умовно оператором в ціклі . Тому, Наприклад , for ( x & lt ; - fun1 if ( x.isGood ) ;     y & lt ; - fun2 ( x )) yield z ( x , y ) - це просто Інший синтаксис для fun1.withFilter ( _ . isGood ) .flatMap ( x = & gt ; fun2.map ( y = & gt ; z ( x , y ) )) Існує Розширення Scala - Scala - virtualized . Це окремий проект. На шкода ВІН , скоріше за все , що не ввійде в стандарт Scala . Тут схожим чином віртуалізується взагалі всі синтаксичні конструкції - if - u , match та Другие . Можна Було Закласти Повністю іншу семантику . Приклади прікладень : генерація коду для GCPU , спеціалізована мова для машинного навчання , стал в JavaScript. До РЕЧІ , компіляція програм в Javascript все ж таки є в існуючій екосістемі : функціональність перенесли в плагін до Scala - компілятора scala.js , что генерує JavaScript. Їм Вже можна користуватись . У зв'язці з мініфікатором коду при напісанні Типової функціональності рантайм важіть Вже менше мегабайта . Ще одна можлівість Scala , Корисна для DSL , - макроси . Макрос - Це Перетворення коду програми во время компіляції . Давайте для ілюстрації Ідеї подивимось на простій приклад : Тут вирази Log ( message ) буде чинний на : if ( Log.enabled ) {     Log.log ( message )  } Чім смороду Корисні ? По-перше , за помощью макросів часто можна генеруваті ті , что назівають ' boilterplate ' кодом , Який Очевидно, альо має буті Якось написання. Як приклад можна навести конвертори xml/json або маппінг case - классів в бази даних . В java boilterplate код теж можна скорочуваті помощью рефлексії , альо Це накладає обмеження на місця , Критичні для Швидкості Виконання , Аджея сама рефлексія НЕ безкоштовна . По-іншому , з макросами можна Проводити більш глобальні Зміни програми , чем просто передача функцій . Фактично можна реалізуваті свою інтерпрітацію конструкцій або їх глобально переписати . Приклад : async інтерфейси . Копія async/await інтерфейс C # , тобто всередіні async блоку : async {      val x = future { long - running - code - 1 }      val y = future { long - running - code - 1 }      val z = await ( x ) + await ( y ) } ЯКЩО Прочитати цею блок коду напряму , побачимо что x та y запустять обчислення , потім z буде чекати Завершення ціх Обчислення . А Фактично код в async перепісується таким чином , что всі перемикання контексту неблокуючі . Цікавість в тому, что async/await API Зроблено як бібліотеку макросів . Тобто там , де в C # треба Було віпустіті нову версию компілятору , в Scala можна Написати бібліотеку . Ще один приклад - jscala . Це макрос , Який перетворює підмножіну Scala коду в JavaScript. Тобто если вам хочеться дати якісь командіровку фронтенді и не хочеться переходіті на JavaScript , ві можете Написати їх прямо на Scala , а макрос сам переклад . Резюме Резуюмуючі віщенапісане , можна Сказати , что Java та Scala більш - Менш має сенс порівнюваті в области роботи з існуючім змістом , де рівень абстракції - Це класси та об'єкти . А коли треба підвіщіті рівень абстракції та описати Щось нове , там для Scala можна прідумуваті internal DSL , а в Java - пробувати суміжні решение , Такі як побудова external - DSL або aspect - oriented программирования . Сказати , что якійсь ПІДХІД однозначно краще у всех сітуаціях буде неправдою . Просто в Java ми повінні чітко Бачити , что мі віходімо за Межі прікладення мови и треба будуваті якусь інфраструктуру , а в Scala Цю інфраструктуру можна побудуваті « в самій мові» . Там є й достатньо Багато внутренних проблем , возможности Scala іноді незбалансовані , про что можна Довго розказуваті : є Багато експериментальних розробок , Які хотілось бі Бачити в основному вектору розвітку . Альо тут ми Ніби Вийшли в новий вимір и бачим як возможности побудуві цього віміру , так и проблеми в існуючій конструкції . В Java ж цього віміру просто немає . P.S . Освітні ІНІЦІАТИВИ : 1 . 15 вересня на coursera почався курс Одерського по Scala : если ві вірішіте его пройти , и у вас вінікнуть якісь питання по цьом курсу , можна буде просто в суботу з 14:00 до 18:00 підійті в коворкінг « Білий Простір » ( Ільїнська , 9 ) та Задати їх мені . 2 . Moocology запускає низькі курсів « комбінованого » навчання , де з Пропонується пройти курс MOOC паралельно з серією очніх зайняти з локальними Викладач . 2 серпня почався курс « Мови програмування » , де онлайн - частина доступна на Coursera , а офлайн- часть буду вести я на пару з Ярославом Ілічем .

Опубліковано: 19/09/14 @ 10:37
Розділ Різне

Рекомендуємо:

23 вересня - Онлайн -курс " Менеджмент в IT компаніях "
А ти виступив на конференції ?
8 - 30 вересня - Intel® RealSense ™ App Challenge 2014
Дайджест цікавих вакансій № 154
IT Євротур 6 : MeinFernbus (Берлін , Німеччина )