Avatar ·

Ошибка "Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6" в Kotlin

Привет всем! Я новичок в Kotlin и Android-разработке. Недавно столкнулся со странной ошибкой при сборке проекта в IntelliJ IDEA. У меня есть простой проект, использующий библиотеку Corda. Когда я пытаюсь скомпилировать проект, Gradle выдает следующую ошибку: ``` Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6 ``` Ошибка возникает в нескольких местах, но чаще всего при вызове inline-функций из стандартной библиотеки Kotlin, например, `let`, `apply` или `also`. Вот пример моего кода: ```kotlin fun processData(data: String?) { data?.let { println("Data is not null: $it") } } fun main() { processData("Hello, Kotlin!") } ``` Я использую Kotlin версии 1.3.72, а в `build.gradle` у меня указано: ```groovy android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_6 targetCompatibility JavaVersion.VERSION_1_6 } } ``` Я читал, что это связано с несоответствием целевых версий JVM, но не понимаю, как это исправить. Почему inline-функции требуют JVM 1.8? И как правильно настроить проект, чтобы избежать этой ошибки? Буду благодарен за подробное объяснение! <<>>

Причина ошибки и её объяснение

Ошибка возникает из-за несоответствия между JVM-таргетами (целевыми версиями JVM) для разных модулей или библиотек в вашем проекте. Конкретно: inline-функции Kotlin (например, let, apply, also) при компиляции вставляют свой байт-код непосредственно в место вызова. Если стандартная библиотека Kotlin (или другая библиотека, например, Corda) была скомпилирована с JVM target 1.8, а ваш проект использует JVM target 1.6, то JVM не может вставить байт-код 1.8 в байт-код 1.6, так как форматы байт-кода различаются (например, в 1.8 появились новые инструкции для лямбд и default-методов в интерфейсах).

Как исправить: пошаговое решение

Самый простой и правильный способ — повысить JVM target вашего проекта до 1.8 (или выше). Начиная с Android API 26 (Android 8.0), это стандарт. Вот как это сделать:

  1. Обновите build.gradle (уровня модуля):
    
    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        kotlinOptions {
            jvmTarget = "1.8"
        }
    }
        
  2. Убедитесь, что версия Kotlin совместима: Используйте Kotlin 1.3.72 или новее (в вашем случае всё в порядке).
  3. Проверьте зависимости: Если вы используете библиотеки, скомпилированные с JVM 1.8 (например, Corda), они будут работать только с JVM target 1.8+.

Почему это работает?

JVM target 1.8 поддерживает все возможности байт-кода, которые используются в inline-функциях Kotlin, включая:

  • Инструкции для invokedynamic (используются для лямбд).
  • Default-методы в интерфейсах.
  • Улучшенную поддержку аннотаций и типов.

После изменения target Gradle перекомпилирует ваш код с правильными инструкциями, и ошибка исчезнет.

Дополнительные советы

  • Для Android: Если вы поддерживаете старые версии (ниже API 26), вы всё равно можете использовать JVM 1.8, так как Android Studio и D8/R8 компиляторы умеют преобразовывать байт-код 1.8 в совместимый с API 16+.
  • Для non-Android проектов: Убедитесь, что ваша среда выполнения (JRE) поддерживает Java 8. Если нет — обновите её.
  • Проверка версии Kotlin: В некоторых старых версиях Kotlin (до 1.2) была проблема с inline-функциями. Обновитесь до последней стабильной версии.

Пример исправленного build.gradle для Android:


apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

android {
    compileSdkVersion 30
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 30
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
<<>>

Альтернативный подход: отключение inline-функций или использование ProGuard

Если по каким-то причинам вы не можете повысить JVM target до 1.8 (например, вы работаете с очень старой системой, которая поддерживает только Java 6), есть обходные пути, но они менее предпочтительны:

  1. Замена inline-функций на обычные: Вместо let, apply, also используйте явные проверки на null или обычные вызовы методов. Например:
    
    fun processData(data: String?) {
        if (data != null) {
            println("Data is not null: $data")
        }
    }
        
    Это решит проблему, но лишит вас удобства Kotlin.
  2. Настройка ProGuard/R8: Включите обфускацию и оптимизацию, которая может переписать inline-функции в обычные вызовы. Добавьте в build.gradle:
    
    android {
        buildTypes {
            release {
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
    }
        
    Но это не гарантирует полного исправления и может усложнить отладку.
  3. Использование Kotlin/Native вместо Kotlin/JVM: Если проект не привязан к JVM, можно перейти на Kotlin/Native, который компилируется в нативный код и не имеет проблем с JVM-таргетами. Однако это радикальное изменение.

Важно: Эти методы — временные костыли. Рекомендуется всегда использовать JVM target 1.8 для современных проектов, так как Java 6 устарела и не поддерживается многими библиотеками.