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

Java - это язык программирования, основной характеристикой которого является ориентация на объект (OO). Это относится к методу программирования и дизайна, который может иметь язык.

Хотя существует множество интерпретаций того, что такое объектная ориентация, первая идея состоит в том, чтобы спроектировать программное обеспечение таким образом, чтобы различные типы данных, которые они используют, были связаны с их операциями. Таким образом, данные и код (функции или методы) объединяются в сущности, называемые объектами.

Что касается Scala, это язык функционального программирования (FP), поэтому функции также являются переменными, и вы можете передавать их другим функциям. Вы можете написать свой код, используя объектно-ориентированный подход, FP или объединить их в гибридном стиле. Исходный код Scala компилируется в файлы «.class», которые запускаются на JVM.

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

Можно ли смешивать код Scala и Java?

Да, есть возможность смешивать оба типа кода. Можно создать проект SBT, поместить код Scala в src / main / scala и код java в src / main / java в одном проекте и заставить его работать.

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

Коллекции для использования в классе Java приложения Scala

Вы используете классы Java в приложении Scala, и эти классы либо возвращают коллекции Java, либо требуют коллекции Java в своих вызовах методов.

Используйте методы объекта JavaConversions в Scala, чтобы преобразование работало. Например, класс java.util.ArrayList обычно используется в приложениях Java, и вы можете имитировать получение ArrayList из метода в REPL, например

def nums = {
var list = new java.util.ArrayList[Int]()
list.add(1)
list.add(2)
list
} 

Несмотря на то, что этот метод написан на Scala при вызове, он действует так же, как если бы он возвращал ArrayList из метода Java:

val list = nums
java.util.ArrayList[Int] = [1, 2]

Однако, поскольку это коллекция Java, я не могу вызвать метод foreach:
list.foreach (println)

Ошибка: значение foreach не является членом java.util.ArrayList [Int] list.foreach (println)

Эту ошибку можно исправить, импортировав объект JavaConversions. Таким образом, указанный выше ArrayList получает метод foreach, который необходимо выполнить, и генерирует следующий вывод:

scala> import scala.colletion.JavaConversions._
import scala.colletion.JavaConversions._
scala> list.foreach(println)
1
2

Пример методов JavaConversions:

Ниже приведен пример метода getNumbers (), который, как мы можем представить, создан в классе Java.

Затем представьте, что мы хотим вызвать метод getNumbers () из кода Scala следующим образом:

val numbers = JavaExamples.getNumbers()
numbers.foreach(println) // this won’t work
//What will happen is that the compiler will generate the following error:
value ‘foreach’ is not a member of java.util.List[Integer]

Это потому, что мы должны импортировать JavaConversions.asScalaBuffer, прежде чем запускать его снова. Когда мы это делаем, мы явно вызываем метод asScalaBuffer

Откровенный призыв

import scala.collection.JavaConversions.asScalaBuffer
val numbers = asScalaBuffer(JavaExamples.getNumbers)
numbers.foreach(println)
// prints ‘scala.collection.convert.Wrappers$JListWrapper’
println(numbers.getClass)

Подразумеваемый звонок

import scala.collection.JavaConversions.asScalaBuffer
val numbers = JavaExamples.getNumbers
numbers.foreach(println)
// prints ‘java.util.ArrayList’
println(numbers.getClass)

Вызовы println (numbers.getClass ) показывают, что есть небольшая разница в результате между явным и неявным использованием.

Использование вызова явного метода asScalaBuffer преобразует числовые объекты в экземпляр collection.convert.Wrappers $ JListWrapper, в то время как неявное использование показывает что числа - ArrayList.

На практике вы можете использовать любой подход, в зависимости от ваших предпочтений при работе с неявными преобразованиями; оба позволяют вызывать foreach, map и другие последовательные методы Scala.

Вы можете повторить тот же пример, используя карту Java и HashMap. Сначала создайте этот метод в классе JavaExamples:

// java public static Map getPeeps() {
Map peeps = new HashMap();
peeps.put(“captain”, “Kirk”);
peeps.put(“doctor”, “McCoy”);
return peeps; }

Затем перед вызовом этого метода из кода Scala импортируйте соответствующий метод JavaConversions:

импорт scala.collection.JavaConversions.mapAsScalaMap

Затем вы можете вызвать метод mapAsScalaMap явно или разрешить его неявный вызов:

// explicit call val peeps1 = mapAsScalaMap(JavaExamples.getPeeps)
// implicit conversion val peeps2 = JavaExamples.getPeeps

Опять же, есть разница между типами объектов на карте. В этом случае peeps1, в котором использовался явный вызов метода, имеет тип коллекции .convert.Wrappers $ JMapWrapper, а peeps2 - java введите .util.HashMap.

Обратите внимание, что класс JavaConversions претерпел несколько изменений, хотя вы увидите большое количество методов преобразования в своей среде IDE, многие из них устарели. См. Последнюю версию Scaladoc для объекта JavaConversions для получения обновленной информации.

Объект Scala JavaConverters позволяет выполнять преобразования, аналогичные показанным примерам, хотя они не предлагают неявных преобразований. Вместо этого они требуют, чтобы вы явно вызывали методы asJava или asScala для выполнения преобразований. Будьте осторожны, потому что объект также содержит много устаревших методов.

Ниже приведен список совместимости коллекций Scala и Java:

scala.collection.Iterable <=> java.lang.Iterable
scala.collection.Iterable <=> java.util.Collection
scala.collection.Iterator <=> java.util.{ Iterator, Enumeration }
scala.collection.mutable.Buffer <=> java.util.List
scala.collection.mutable.Set <=> java.util.Set
scala.collection.mutable.Map <=> java.util.{ Map, Dictionary }
scala.collection.mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap

Во всех случаях преобразование исходного типа в целевой и обратно вернет исходный исходный объект, например.

import scala.collection.JavaConversions._
val sl = new scala.collection.mutable.ListBuffer[Int]
val jl : java.util.List[Int] = sl
val sl2 : scala.collection.mutable.Buffer[Int] = jl
assert(sl eq sl2)

Кроме того, предусмотрены следующие односторонние преобразования:

scala.collection.Seq => java.util.List
scala.collection.mutable.Seq => java.util.List
scala.collection.Set => java.util.Set
scala.collection.Map => java.util.Map
java.util.Properties => scala.collection.mutable.Map[String, String]

Надеюсь, эта статья была полезной.

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

Ссылки

(JavaConversions) https://www.scala-lang.org/api/2.9.3/scala/collection/JavaConversions$.html

((JavaConversions) https://www.scala-lang.org/api/current/index.html#scala.collection.JavaConversions$

(JavaConverters) https://www.scala-lang.org/api/current/index.html#scala.collection.JavaConverters$