توافق JVM في Gradle: كيفية التوفيق بين Kotlin 1.8 وJava 11

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

لماذا ينهار مشروعك فجأة على Kotlin وJava؟

\n\n

هل سبق لك أن واجهت موقفًا تكتب فيه كودًا بلغة Kotlin، وتستخدم Java 11 للكود الأساسي، وفجأة يظهر خطأ توافق من Gradle؟ مألوف؟ هذه واحدة من أكثر المشكلات شيوعًا عند إعداد المشاريع متعددة الوحدات. خاصة عند استخدام build.gradle.kts (Kotlin DSL) ومحاولة التوفيق بين compileJava وcompileKotlin. في هذه المقالة، سنشرح كيفية ضبط توافق هدف JVM بشكل صحيح لـ Java 11 وKotlin 1.8، بحيث يتم بناء مشروعك بدون أخطاء. نعم، سنفعل ذلك بالعربية، مع لمسة فكاهية وبدون نظرية مملة.

\n\n

إذا أتيت من Stack Overflow، فأنت تعلم أن السؤال الأصلي حصد آلاف المشاهدات. نحن نُكيّفه ليتناسب مع الشكل التعليمي، بحيث لا تكتفي بنسخ الإجابة، بل تفهم سبب ظهور الخطأ وكيفية تجنبه في المستقبل.

\n\n

المشكلة: عندما تتحدث Java وKotlin بلغتين مختلفتين

\n\n

تخيل أن Java 11 هي أستاذ صارم يطلب أن يكون كل شيء وفقًا للمعيار. أما Kotlin 1.8 فهو زميله المبدع الذي يريد استخدام ميزات جديدة، لكنه ينسى إبلاغ الأستاذ. النتيجة: صراع.

\n\n

في Gradle، كل مهمة ترجمة (compileJava، compileKotlin) لها هدف JVM خاص بها - إصدار البايت كود الذي يُترجم إليه الكود. إذا لم يتطابقا، ستحصل على خطأ مثل:

\n\n
Error: Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.8.0, expected version is 1.6.0.\n
\n\n

أو ما هو أسوأ - انخفاض صامت في الأداء بسبب عدم توافق البايت كود.

\n\n

لماذا يحدث هذا تحديدًا في build.gradle.kts؟

\n\n

Kotlin DSL (ملفات .gradle.kts) هي أداة قوية، لكنها تتطلب تحديد المعاملات بشكل صريح. على عكس Groovy DSL حيث يتم "التقاط" بعض الإعدادات تلقائيًا، في Kotlin DSL يجب أن تكون دقيقًا. هذا ينطبق بشكل خاص على هدف JVM.

\n\n

الخطأ النمطي: تقوم بتعيين java.targetCompatibility = JavaVersion.VERSION_11، لكنك تنسى تحديد نفس الشيء لـ Kotlin. أو العكس - تقوم بإعداد compileKotlin، بينما يبقى compileJava على Java 8.

\n\n

الحل الصحيح: هدف JVM موحد لـ Java وKotlin

\n\n

لتجنب الأخطاء، يجب تحديد هدف JVM بشكل صريح لكلتا المهمتين. إليك كيفية القيام بذلك في build.gradle.kts:

\n\n

الخطوة 1: إعداد Java (compileJava)

\n\n

أضف في كتلة java {} أو في tasks.withType<JavaCompile>():

\n\n
java {\n    sourceCompatibility = JavaVersion.VERSION_11\n    targetCompatibility = JavaVersion.VERSION_11\n}\n\ntasks.withType<JavaCompile>() {\n    options.encoding = "UTF-8"\n    options.compilerArgs.add("-parameters")\n}\n
\n\n

لاحظ: يجب أن يكون sourceCompatibility وtargetCompatibility متطابقين. إذا كنت تكتب كودًا بلغة Java 11، فيجب أن يكون البايت كود أيضًا Java 11.

\n\n

الخطوة 2: إعداد Kotlin (compileKotlin)

\n\n

الآن الجزء الأهم - تحديد هدف JVM لـ Kotlin. يتم ذلك من خلال كتلة kotlin {} أو من خلال tasks.withType<KotlinCompile>():

\n\n
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile\n\ntasks.withType<KotlinCompile>() {\n    kotlinOptions {\n        jvmTarget = "11"  // انتبه: هذه سلسلة نصية، وليست JavaVersion!\n        apiVersion = "1.8"\n        languageVersion = "1.8"\n    }\n}\n
\n\n

لماذا jvmTarget عبارة عن سلسلة نصية؟ لأن Kotlin تتوقع تمثيلًا نصيًا للإصدار (مثل "1.8"، "11"، "17"). إذا حاولت تمرير JavaVersion.VERSION_11، فسيصدر المترجم خطأ.

\n\n

مثال كامل لملف build.gr

المدونات

توصيات الكتب