На этом шаге мы рассмотрим более подробно, как обрабатываются исключения.
Вызов функции Kotlin из Java требует дополнительного понимания, как обрабатываются исключения. Все исключения в Kotlin, как уже упоминалось, непроверяемые. Но в Java это не так - там исключения проверяемые, и они должны обрабатываться, чтобы устранить риск сбоя. Как это влияет на вызов функций Kotlin из Java?
Чтобы узнать это, добавьте в Hero.kt функцию acceptApology(). Настало время расплаты для монстра.
. . . . . fun makeProclamation() = "Greetings, beast!" fun handOverFood(leftHand: String = "berries", rightHand: String = "beef") { println("Mmmm... you hand over some delicious $leftHand and $rightHand.") } fun acceptApology() { throw IOException() } class Spellbook { . . . . . }
Рис.1. Возбуждение непроверяемого исключения (Hero.kt)
Теперь вызовите acceptApology() из Jhava.java.
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class Jhava { . . . . public void extendHandInFriendship() throws Exception { throw new Exception(); } public void apologize() { try { Hero.acceptApology(); } catch (IOException e) { System.out.println("Caught!"); } } }
Рис.2. Возбуждение исключения в Java (Jhava.java)
Jhava-монстр достаточно умен, чтобы заподозрить героя в обмане, и заключает вызов acceptApology() в блок try/catch. Но компилятор Java предупреждает вас о том, что появление исключения IOException в блоке try, то есть в acceptApology(), невозможно. Почему так? Ведь acceptApology() явно возбуждает исключение.
Чтобы понять причину, рассмотрим скомпилированный байт-код Java:
public static final void acceptApology() { throw (Throwable)(new IOException()); }
Как видите, IOException точно возбуждается в этой функции, но в ее сигнатуре нет ничего, что говорило бы о необходимости проверить IOException. Именно поэтому компилятор Java уверенно заявляет, что acceptApology() не возбуждает IOException при вызове из Java, - он просто не знает об этом.
К счастью, есть аннотация @Throws, которая решает эту проблему. Когда вы используете @Throws, вы включаете информацию об исключении, которое возбуждается функцией. Добавьте аннотацию @Throws в acceptApology(), чтобы добавить в байт-код Java эту важную информацию.
. . . . . fun makeProclamation() = "Greetings, beast!" fun handOverFood(leftHand: String = "berries", rightHand: String = "beef") { println("Mmmm... you hand over some delicious $leftHand and $rightHand.") } @Throws(IOException::class) fun acceptApology() { throw IOException() } class Spellbook { . . . . . }
Рис.3. Использование аннотации @Throws (Hero.kt)
Теперь посмотрим на получившийся байт-код Java:
public static final void acceptApology() throws IOException { throw (Throwable)(new IOException()); }
Аннотация @Throws добавляет ключевое слово throws в Java-версию acceptApology(). Вернитесь в Jhava.java и убедитесь, что компилятор Java теперь удовлетворен, так как понял, что в acceptApology() возбуждает исключение IOException, которое необходимо проверить.
Аннотация @Throws сглаживает идеологическую разницу между Java и Kotlin в отношении проверки исключений. Если вы пишете Kotlin API, который может вызываться из Java, используйте эту аннотацию, чтобы тот, кто будет вызывать ваш код, мог грамотно обработать любое возбужденное исключение.
На следующем шаге мы рассмотрим функциональные типы в Java.