Статьи

Почему Java люди должны смотреть вперед в Scala

Выполняется интересная серия постов в блоге: почему Java-пользователи перестают смотреть на C #: часть 1 и часть 2 (на момент написания этого поста). Он предлагает интересный и подробный набор контрастов между Java и C #. Это подробный анализ и делает его очень полезным для чтения. Что действительно заинтриговало меня, так это комментарий:

«Мы замечаем, что разработчики Java обычно склонны презрительно смотреть на C #, как на подражателя, созданного Microsoft и используемого манекенами. В этой серии блогов я постараюсь охватить эту чушь и показать некоторые достоинства C # ».

По крайней мере, по моему опыту, я не вспоминаю разработчиков Java, которые презрительно смотрели на C # (обратите внимание, что я не упомянул .NET, Windows или Microsoft — я сказал C #). Но, возможно, есть некоторые, и серия блогов действительно пытается предоставить им достаточные доказательства, чтобы пересмотреть их мнение. Но чтение постов заставило меня задуматься, есть хотя бы одна веская причина, по которой Java-программисты могут предпочесть праздновать и с нетерпением ждать, а не беспокоиться о Java. И все эти возможности доступны для запроса на их предпочтительной платформе — JVM, а также с возможностью взаимодействия и постепенного переноса из их существующих кодовых баз. — Скала.

Теперь, поверьте мне, возможности scala выходят далеко за рамки описанных ниже. Но это дальнейшее открытие — приключение, которое читателям предлагается провести позже. Кроме того — я только немного на этом пути открытия Scala и все еще должен преодолеть расстояния. Поэтому я не удивлюсь, если люди смогут предложить более здоровые и превосходные решения, чем те, которые я описал ниже. На самом деле, я бы посоветовал им сделать то же самое. Но вот что вам всем, жителям Ява, нравится.

Пожалуйста, обратите внимание, что будет полезно, если вы просмотрите эти два сообщения в блоге, упомянутых выше — поскольку почти каждый пример, на который я ссылаюсь ниже, который я пишу с использованием Scala, основан на примерах в этих сообщениях, написанных на Java и C #. Таким образом, даже после прочтения этих сообщений может быть полезно оставить их открытыми в других вкладках. Так что, если вы предпочитаете это делать, вы можете ссылаться на эти примеры Java и C # одновременно с чтением примеров Scala.

Единая система типов

Scala имеет типы для Java-примитивов, например. Byte, Short, Int. Таким образом, вы можете продолжать работать с ними как с объектами, вместо того, чтобы иметь дело с ними как с примитивами. Все классы значений наследуются от AnyVal, тогда как все остальные наследуют от AnyRef, оба в свою очередь наследуются от Any. Кроме того, существует ряд дополнительных методов, доступных для этих типов (например, RichInt ). В то же время эти типы имеют точно такие же диапазоны , как ява примитивов. Таким образом, компилятор scala может выбрать преобразование экземпляров таких типов значений в соответствующие типы примитивов Java.

Прощальные Проверенные Исключения

Прощай. Прощайте. Au revoir, Vale. Проверенные исключения были прощаны. И я не думаю, что кто-то менее рад этому. Но если вам нужно вызывать Scala-код из Java, вы можете использовать аннотацию @throws, чтобы пометить ваши методы, чтобы java-код мог обрабатывать их как выброшенные исключения.

Двойные радуги

Еще один тяжелый образец, от которого до свидания отказались, — сеттеры в стиле бобов Java. Каждый объявленный не закрытый член автоматически получает метод получения и установки по умолчанию бесплатно. В ситуациях, когда вы захотите переопределить поведение по умолчанию, вы также можете сделать это. Кроме того, конкретная конструкция, называемая case-классом, еще больше упрощает это и помогает вам создать класс, тривиально с разумной реализацией equals, toString и т. Д., Которые уже внедрены. Вполне вероятно, что новые клавиатуры продолжают сохранять свой блеск и продолжают функционировать гораздо дольше, когда кодирование в Scala, чем в Java. Простой пример двойных радужных аксессуаров:

case class Meme (var catchPhrase : String,  var url: String)

// Notice: class members are declared as default constructor parameters. No further
// declaration required again.
class MemeAdvanced (private [this] var cp: String, private [this] var u: String){
// Since these are private fields, they can be wrapped using
// getters / setters as follows which can be modified to suit any non-typical
// expectations eg. validations

// if the above parameter declarations did not have private qualifier,
// the methods below would be automatically provided, whereas if declaration contained
// val instead of var, only the getter would get provided.
def catchPhrase = cp
def catchPhrase_=(s: String) {cp = s}

def url = u
def url_=(s: String) { u = s}
}

object DoubleRainbow {

def main(args : Array[String]) : Unit = {
// using case classes
var meme = new Meme("foo","bar")
meme.catchPhrase = "Rick roll'd"
meme.url = "http://www.youtube.com/watch?v=EK2tWVj6lXw"
println(meme.catchPhrase)

// using normal classes
var meme2 = new MemeAdvanced("foo","bar")
meme2.catchPhrase = "Rick roll'd"
meme2.url = "http://www.youtube.com/watch?v=EK2tWVj6lXw"
println(meme.catchPhrase)
}
}

инициализаторов

Там есть куча новых возможностей инициализации, например. инициализация с использованием имен полей

var meme = new Meme(catchPhrase="blub", url="blub2")
println(meme.catchPhrase)

или вы можете инициализировать коллекцию, передав серию аргументов в конструктор

    val digits = List[Int](0,1,2,3,4,5,6,7,8,9)

или вы можете загрузить карту с кучей ассоциаций во время создания

val keywordsMapping = Map[String,String] ( "super" -> "base", "boolean" -> "bool", "import" -> "using" )
println(digits.head + " " + keywordsMapping("boolean"))

Строковые (многострочные) строки

Это не так уж сложно со скалой. Просто используйте тройной последовательный разделитель двойных кавычек («» »), чтобы строка занимала несколько строк.

val input = """Multiline
325-532-4521"""
println(input)

Методы как первоклассные граждане

Отсутствуют указатели на функции после того, как вы перешли из C / C ++? Это тоже доступно .. в безопасном виде.

// declare a function pointer which takes a string as an input
// and outputs the same
var normalizeOp = (input: String) => input reverse;
println(normalizeOp("abcd"))

// compiler error : normalizeOp = (input: String) => 0;
// thus the type of normalizeOp can no longer be changed

// but it can be used to point to a different method instead
normalizeOp = (input: String) => input trim;
println(normalizeOp(" foo bar "))

Мероприятие

Теперь в Scala нет встроенного класса Event, который имеет возможности автоматической подписки для проводной публикации. Но одна из тем, которые вы найдете в этом посте, заключается в том, что Scala оправдывает свое название, которое на самом деле относится к масштабируемому языку. Таким образом, целый набор языковых конструкций может быть встроен в язык путем написания другого кода. Вот почему иногда это выглядит как метаязык, язык для написания ваших собственных языковых структур. Конструкция ниже довольно проста и понятна, но позже мы увидим создание вашей собственной структуры управления языком, такой как конструкции.

Вот простой класс событий

class Event {
var l = List[() => Unit]()

def +=(f: () => Unit): Unit = l = f :: l

def apply() = l map (_())
}

Он объявляет список слушателей l и позволяет регистрировать прослушивающие функции по инициируемому событию (которое в этом случае будет методом + =. Наконец, так как событие инициируется с помощью конструкции event () в C #, мы здесь определяем apply функция, которая будет запускаться всякий раз, когда вызывается e (), e является экземпляром любого события. Примечание: я намеренно использовал имена отдельных символов для полей, чтобы позволить вам сосредоточиться на других конструкциях — не очень хорошая практика кодирования.

class Button {
val action = new Event();

def performClick() = onClick()

private def onClick() = {
// this should call the apply() method
action()
}
}

object EventDriver {
def performAction() {
println("Work!")
}

def main(args : Array[String]) : Unit = {
val button = new Button()
// add a method as a listener
button.action += performAction
// trigger the event
button.performClick()
}
}

Так действительно ли нужно, чтобы событие было запечатлено на языке? Очевидно, не совсем, когда это масштабируемый язык.

Лямбда-выражения

Действительно одна из сильных сторон этого языка. Простая лямбда-функция приведена ниже

    def isUniverseAnswer = (x: Int) => x == 42
println(isUniverseAnswer(42))
println(isUniverseAnswer(43))

Там это не так сложно. Но что, когда вы хотите определить лямбду на лету? Оказывается, это тоже прямо вперед.

  val users = List[User](User("Five",5), User("Fifteen",15), User("TwentyFive", 25),
                            User("Ten",10), User("Twenty", 20))
    // Note : the argument passed to the filter() method is the lambda, and the _ refers
    //           to each User object passed in
    println (users filter (_.age < 18))

И если мы хотим исследовать замыкания, там тоже есть хорошая поддержка.

  var counter = 0
val action = () => {counter += 1; println("counter = " + counter)}
action()
action()

Как сказал бы Джек Воробей, подкованный?

Методы расширения

Точные детали того, как некоторые из этих возможностей работают, выходят за рамки этого поста, но scala позволяет использовать множество новых методов расширения для базовых встроенных типов с помощью различных дополнительных классов, и эти же возможности можно использовать. вами, чтобы определить любые дополнительные методы расширения. В качестве примера тизера мы будем использовать обратный метод, определенный в типе WrappedString, который помогает расширить тот же метод, который будет использоваться со строками.


1

println
(
«Пример» обратный
)

Возврат нескольких значений

Это очень интуитивно понятно и просто.

object ReturnMultiple {
// this method returns two values
def addOneToAll(a: Int, b: Int) = (a + 1, b + 1)

def main(args : Array[String]) : Unit = {
val (c, d) = addOneToAll(3,4)
println(c + "," + d)
}
}

Нулевой оператор

Насколько я знаю, у scala нет этого оператора. Код на C # выглядит следующим образом, что позволяет в качестве результата установить первое ненулевое значение среди a, b и c.

string result = a ?? b ?? c

Что ж, мы построим этот оператор (я уверен, что кто-то предложит лучший вариант, чем приведенный ниже)

class Nullable(val t: AnyRef) {
def ??(b: AnyRef) = if (this.t != null) this.t else b
}

object NullCoalescing {
implicit def objToNullable(a: AnyRef) : Nullable = { new Nullable(a) }

def main(args : Array[String]) : Unit = {
println("hello" ?? null ?? null)
println((null: AnyRef) ?? "world" ?? null)
println((null: AnyRef) ?? null ?? "foobar")
}
}

Если вы видите три оператора println, они показывают, как вновь определены ?? Оператор может предоставить ту же семантику. Поскольку null не может помочь механизму вывода типов, в последних двух операторах println он был явно приведен к AnyRef.

Автоматическое управление ресурсами

Очевидно, C # имеет встроенную возможность автоматического управления ресурсами с помощью ключевого слова using . Увы, Скала этого не делает. Но построить это вряд ли много усилий. Итак, здесь идет:

import java.io.FileInputStream
import java.io.DataInputStream
import java.io.InputStreamReader
import java.io.BufferedReader
object AutomaticResourceManagement {
// This is a structural type. Any type which has methods which match the two
// desired signatures specified below can be used wherever this type is expected
// ** no inheritance required **
type ReadableAndCloseable = { def close(): Unit ; def readLine() : String}

// And to be able to define our own control structure, the second parameter to
// using takes a function block which takes a ReadableAndCloseable as input
// and returns nothing.
def using(resource: ReadableAndCloseable)(f: ReadableAndCloseable => Unit) {
try {
f(resource)
} finally {
println("Closing resource")
resource.close()
}
}

def main(args : Array[String]) : Unit = {
val reader = new BufferedReader(new InputStreamReader(new DataInputStream(new FileInputStream("foo.txt"))))
// Now you can be sure, the reader will get closed.
using(reader){
(resource) => println("First line is: " + resource.readLine())
}
}
}

Резюме

Ну, есть еще в Scala. Еще больше. И это требует терпеливых усилий, чтобы выучить это. Мы едва начали говорить о возможностях функционального программирования. Или его параллельные коллекции. Или для этого имеет значение его сопоставление с образцом. Или даже его способность иметь дело с Generics с указанным Co и Contravariance. Мы не зашли так далеко. Но все, казалось бы, далекие возможности — которые, казалось, были в нескольких милях от другой планеты, называемой .NET, и страны, называемой C #, на самом деле находятся всего в нескольких шагах — используя Scala. Как программист Java, я не думаю, что вы должны смотреть вниз на C # .. просто с нетерпением жду Scala:)

От http://blog.dhananjaynene.com/2011/05/why-java-folks-should-look-forward-to-scala/