На прошлой неделе я написал пост о запуске класса Java в качестве подпроцесса . Этот пост был вызван моей необходимостью запустить класс из теста без предварительной сборки Jar. Единственная разница между тем, что я написал в этом посте, и тем, что произошло на самом деле, была языком. Я использовал Котлин, чтобы написать этот тест. Не Java. Поэтому я решил написать этот пост, который основывается на том, что я ранее написал, и фокусируется на запуске подпроцесса Kotlin вместо подпроцесса Java.
Давайте начнем с эквивалентной реализации exec из Запуска Java-класса как подпроцесса :
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
@Throws(IOException::class, InterruptedException::class)fun exec(clazz: Class<*>, args: List<String> = emptyList(), jvmArgs: List<String> = emptyList()): Int { val javaHome = System.getProperty("java.home") val javaBin = javaHome + File.separator + "bin" + File.separator + "java" val classpath = System.getProperty("java.class.path") val className = clazz.name val command = ArrayList<String>() command.add(javaBin) command.addAll(jvmArgs) command.add("-cp") command.add(classpath) command.add(className) command.addAll(args) val builder = ProcessBuilder(command) val process = builder.inheritIO().start() process.waitFor() return process.exitValue()} |
Краткое объяснение, так как я рассказал обо всем остальном в моем предыдущем посте
Путь к исполняемому файлу Java извлекается и сохраняется в javaBin . Используя это вместе с переданными в clazz , args , jvmArgs и classpath процесса, команда ProcessBuilder создает и выполняет ProcessBuilder . Теперь класс успешно работает как подпроцесс.
Приведенный выше код в значительной степени является копией и вставкой реализации Java. Различия — это порядок параметров функции. Я решил переключить args и jvmArgs так, чтобы я мог в полной мере использовать их значения по умолчанию. Это основано на предположении, что вы скорее предоставите args вместо jvmArgs . При запуске из Kotlin это не сильно беспокоит, так как вы можете называть параметры при вызове функции. Но если какой-либо Java-код должен вызывать эту функцию, тогда может быть полезен порядок параметров (а также добавление @JvmOverloads ).
Ниже приведены некоторые способы вызова exec :
|
1
2
3
4
5
6
7
|
exec(MyProcess::class.java, listOf("3"), listOf("-Xmx200m"))// jvmArgs = emptyList()exec(MyProcess::class.java, listOf("3"))// args = emptyList()exec(MyProcess::class.java, jvmArgs = listOf("-Xmx200m"))// both args and jvmArgs = emptyList()exec(MyProcess::class.java) |
При написании на Kotlin вместо Java заметной разницей является количество способов определения main функции. Это связано с тем, что Kotlin предоставляет много разных маршрутов для создания статической функции.
Функция, которую я предоставил выше, будет запускать main функцию в двух следующих сценариях:
Внутри объекта-компаньона и @JvmStatic
|
1
2
3
4
5
6
7
8
|
class MyProcess { companion object { @JvmStatic fun main(args: Array<String>) { // do stuff } }} |
Внутри объекта и @JvmStatic
|
1
2
3
4
5
6
|
object MyProcess { @JvmStatic fun main(args: Array<String>) { // do stuff }} |
Еще один способ создать статическую функцию в Kotlin — это определить ее вне класса:
|
1
2
3
4
5
6
7
|
@file:JvmName("MyProcess")package dev.lankydanfun main(args: Array<String>) { // do stuff} |
Необходимо указать имя, чтобы Java знала, что с ним делать. Выполнение main функции в этом фрагменте — одна из ситуаций, где это необходимо. К сожалению, функция exec не будет работать в этой ситуации, так как класс MyProcess фактически не существует. Предотвращение его компиляции. Как ни странно, вызов MyProcess из кода Java будет компилироваться без каких-либо изменений.
Чтобы устранить ошибку компиляции, обнаруженную в версии Kotlin, параметры exec нужно изменить очень незначительно:
|
1
2
3
4
|
@Throws(IOException::class, InterruptedException::class)fun exec(className: String, args: List<String> = emptyList(), jvmArgs: List<String> = emptyList()): Int { // className passed into command} |
Это изменение позволяет избежать ошибки компиляции, которую представляет исходная функция exec . Но это связано с недостатком того, что на самом деле не является типобезопасным, и может привести к нескольким ошибкам здесь и там, где передается неверная строка. Как вы можете видеть ниже:
|
1
|
exec("dev.lankydan.MyProcess", listOf("argument"), listOf("-Xmx200m")) |
Что-то в этом изменении действительно шуршит в моих уловках. Поэтому обеспечение обеих перегрузок является, пожалуй, самым разумным решением.
Подводя итог, хотя большая часть того, что я должен сказать о выполнении классов Kotlin в качестве подпроцессов, уже была рассмотрена в разделе Запуск класса Java в качестве подпроцесса . Есть несколько различий из-за гибкости Kotlin. main функция в Kotlin может быть определена несколькими способами, и, к сожалению, одна из тех, кто не ладит с exec функцией, удовлетворяет остальных. К счастью, решение — это небольшое изменение кода, которое может хорошо работать, если есть перегрузки для имен классов String и Class .
Если вам понравился этот пост или вы нашли его полезным (или и тем, и другим), пожалуйста, не стесняйтесь, следите за мной в Твиттере на @LankyDanDev и не забудьте поделиться с кем-либо, кто может найти это полезным!
|
Опубликовано на Java Code Geeks с разрешения Дэна Ньютона, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Запуск класса Kotlin в качестве подпроцесса Мнения, высказанные участниками Java Code Geeks, являются их собственными. |