Взаимодействие с Java и обзор ClojureScript

Используем мощь JVM из Clojure и узнаём, как тот же язык работает в браузере.

Java interop — встроенный синтаксис Clojure для вызова методов, конструкторов и полей классов Java напрямую.

Почему interop важен

Clojure не пытается переписать весь мир. Огромная экосистема Java — работа с датами, сетью, криптографией, базами данных — доступна прямо из Clojure. Это одна из причин выбрать его для бэкенда: зрелые библиотеки уже есть.

Синтаксис interop

Есть несколько форм. Вызов метода — точка перед именем метода; создание объекта — new или точка после имени класса; статический член — слэш.

; вызов метода объекта: (.метод объект аргументы)
(.toUpperCase "clojure")        ; => "CLOJURE"
(.length "привет")              ; => 6

; статический метод: (Класс/метод аргументы)
(Math/sqrt 144)                 ; => 12.0
(Math/max 7 12)                 ; => 12

; создание объекта: (new Класс) или (Класс. аргументы)
(new java.util.Random)          ; => объект Random
(java.util.ArrayList.)          ; => пустой ArrayList

Вывод:

"CLOJURE"
6
12.0
12

Цепочки вызовов

Для нескольких вызовов по объекту удобен макрос doto или уже знакомый ->:

; doto вызывает методы по одному объекту и возвращает сам объект
(doto (java.util.ArrayList.)
  (.add "a")
  (.add "b"))
; => ArrayList ["a", "b"]

ClojureScript: тот же язык в браузере

ClojureScript — это Clojure, который компилируется не в байт-код JVM, а в JavaScript. Вы пишете на том же языке (те же неизменяемые структуры, функции, макросы), но код исполняется в браузере или Node.js. Так одна команда может писать и бэкенд (Clojure на JVM), и фронтенд (ClojureScript), переиспользуя логику и формат данных EDN.

ClojureClojureScript
Компилируется вбайт-код JVMJavaScript
Где исполняетсясервер, JVMбраузер, Node.js
Interopс Javaс JavaScript и DOM
Применениебэкенд, обработка данныхфронтенд, SPA

Как работает под капотом

Поскольку Clojure компилируется в байт-код JVM, его значения — это объекты JVM, а функции — классы. Поэтому interop не требует моста или сериализации: вызов Java-метода — это прямой вызов на JVM. ClojureScript использует тот же подход, но целевая платформа — JavaScript: его компилятор (через Google Closure Compiler) превращает код в оптимизированный и минимизированный JS, отсекая неиспользуемые части.

Частые ошибки

  • Путать формы interop. Точка перед именем — метод объекта; слэш — статический член; точка после класса — конструктор.
  • Ждать неизменяемости от Java-объектов. ArrayList и прочие объекты Java изменяемы — за пределами Clojure-структур гарантий нет.
  • Думать, что ClojureScript — отдельный язык. Это тот же Clojure с другой целевой платформой и interop с JS вместо Java.

Итоги

  • Java interop даёт прямой доступ ко всей экосистеме JVM из Clojure.
  • Формы: (.метод объект), (Класс/статик), (Класс.) для конструктора, doto для цепочек.
  • ClojureScript — тот же язык, компилируемый в JavaScript для браузера.
  • Единый язык на бэкенде и фронтенде с общими структурами данных и EDN.
Проверьте себя
1. Как вызвать метод toUpperCase у строки через Java interop?
A(toUpperCase "clojure")
B(.toUpperCase "clojure")
C("clojure".toUpperCase)
D(String/toUpperCase "clojure")
2. Во что компилируется ClojureScript?
AВ байт-код JVM
BВ JavaScript
CВ нативный машинный код
DВ WebAssembly напрямую
3. Как обратиться к статическому методу sqrt класса Math?
A(.sqrt Math 144)
B(Math/sqrt 144)
C(new Math 144)
D(sqrt Math 144)