এই ডকুমেন্টটি জাভা এবং কোটলিনে পাবলিক এপিআই রচনার জন্য কিছু নিয়মের সমষ্টি, যার উদ্দেশ্য হলো কোডটি যখন অন্য ভাষা থেকে ব্যবহার করা হবে তখন তা স্বাভাবিক বা প্রচলিত রীতিসম্মত মনে হবে।
জাভা (কোটলিন ব্যবহারের জন্য)
কোন কঠিন কীওয়ার্ড নেই
মেথড বা ফিল্ডের নাম হিসেবে কোটলিনের কোনো হার্ড কীওয়ার্ড ব্যবহার করবেন না। কোটলিন থেকে কল করার সময় এস্কেপ করার জন্য এগুলোর ক্ষেত্রে ব্যাকটিক ব্যবহার করা আবশ্যক। সফট কীওয়ার্ড , মডিফায়ার কীওয়ার্ড এবং বিশেষ আইডেন্টিফায়ার ব্যবহার করা যাবে।
উদাহরণস্বরূপ, কোটলিন থেকে ব্যবহার করার সময় মকিটোর ' when ফাংশনে ব্যাকটিকের প্রয়োজন হয়:
val callable = Mockito.mock(Callable::class.java)
Mockito.`when`(callable.call()).thenReturn(/* … */)
Any এক্সটেনশন নাম পরিহার করুন
অত্যন্ত জরুরি না হলে, মেথডের জন্য Any এর এক্সটেনশন ফাংশনের নাম অথবা ফিল্ডের জন্য Any এর এক্সটেনশন প্রপার্টির নাম ব্যবহার করা থেকে বিরত থাকুন। যদিও মেম্বার মেথড এবং ফিল্ডগুলো Any এর এক্সটেনশন ফাংশন বা প্রপার্টির চেয়ে সর্বদা অগ্রাধিকার পাবে, কোড পড়ার সময় কোনটি কল করা হচ্ছে তা বোঝা কঠিন হতে পারে।
নালযোগ্যতা টীকা
একটি পাবলিক এপিআই-এর প্রতিটি নন-প্রিমিটিভ প্যারামিটার, রিটার্ন এবং ফিল্ড টাইপে একটি নালিবিলিটি অ্যানোটেশন থাকা উচিত। অ্যানোটেশনবিহীন টাইপগুলোকে "প্ল্যাটফর্ম" টাইপ হিসেবে গণ্য করা হয়, যেগুলোর নালিবিলিটি অস্পষ্ট।
ডিফল্টরূপে, কোটলিন কম্পাইলার JSR 305 অ্যানোটেশনগুলোকে সম্মান করে, কিন্তু সেগুলোকে সতর্কবার্তা হিসেবে চিহ্নিত করে। আপনি একটি ফ্ল্যাগ সেট করে কম্পাইলারকে অ্যানোটেশনগুলোকে ত্রুটি হিসেবে গণ্য করার নির্দেশও দিতে পারেন।
ল্যাম্বডা প্যারামিটার শেষ
SAM রূপান্তরের জন্য যোগ্য প্যারামিটার প্রকারগুলি শেষে থাকা উচিত।
উদাহরণস্বরূপ, RxJava 2-এর Flowable.create() মেথডের সিগনেচারটি নিম্নরূপে সংজ্ঞায়িত করা হয়:
public static <T> Flowable<T> create(
FlowableOnSubscribe<T> source,
BackpressureStrategy mode) { /* … */ }
যেহেতু FlowableOnSubscribe SAM রূপান্তরের জন্য উপযুক্ত, তাই কোটলিন থেকে এই মেথডের ফাংশন কলগুলো দেখতে এইরকম হয়:
Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)
তবে, মেথড সিগনেচারে প্যারামিটারগুলো উল্টে গেলে, ফাংশন কলগুলোতে ট্রেইলিং-ল্যাম্বডা সিনট্যাক্স ব্যবহার করা যেত:
Flowable.create(BackpressureStrategy.LATEST) { /* … */ }
প্রপার্টি প্রিফিক্স
কোটলিনে কোনো মেথডকে প্রপার্টি হিসেবে উপস্থাপন করতে হলে, কঠোর 'বিন' (bean) স্টাইলের প্রিফিক্সিং ব্যবহার করতে হবে।
অ্যাক্সেসর মেথডের জন্য ` get প্রিফিক্স প্রয়োজন হয়, অথবা বুলিয়ান রিটার্নকারী মেথডের ক্ষেত্রে ` is প্রিফিক্স ব্যবহার করা যেতে পারে।
public final class User {
public String getName() { /* … */ }
public boolean isActive() { /* … */ }
}
val name = user.name // Invokes user.getName()
val active = user.isActive // Invokes user.isActive()
সংশ্লিষ্ট মিউটেটর মেথডগুলোর জন্য একটি set প্রিফিক্স প্রয়োজন।
public final class User {
public String getName() { /* … */ }
public void setName(String name) { /* … */ }
public boolean isActive() { /* … */ }
public void setActive(boolean active) { /* … */ }
}
user.name = "Bob" // Invokes user.setName(String)
user.isActive = true // Invokes user.setActive(boolean)
আপনি যদি মেথডগুলোকে প্রোপার্টি হিসেবে প্রকাশ করতে চান, তাহলে has , set বা get প্রিফিক্সবিহীন অ্যাক্সেসরের মতো অপ্রচলিত প্রিফিক্স ব্যবহার করবেন না। অপ্রচলিত প্রিফিক্সযুক্ত মেথডগুলোও ফাংশন হিসেবে কল করা যায়, যা মেথডটির আচরণের ওপর নির্ভর করে গ্রহণযোগ্য হতে পারে।
অপারেটরের উপর অতিরিক্ত চাপ
এমন মেথডের নাম ব্যবহারের ক্ষেত্রে সতর্ক থাকুন যেগুলো বিশেষ কল-সাইট সিনট্যাক্স (যেমন কোটলিনে অপারেটর ওভারলোডিং ) ব্যবহারের সুযোগ দেয়। নিশ্চিত করুন যে এই ধরনের মেথডের নামগুলো সংক্ষিপ্ত সিনট্যাক্সের সাথে ব্যবহারের জন্য অর্থবহ হয়।
public final class IntBox {
private final int value;
public IntBox(int value) {
this.value = value;
}
public IntBox plus(IntBox other) {
return new IntBox(value + other.value);
}
}
val one = IntBox(1)
val two = IntBox(2)
val three = one + two // Invokes one.plus(two)
কোটলিন (জাভা ব্যবহারের জন্য)
ফাইলের নাম
যখন কোনো ফাইলে শীর্ষ-স্তরের ফাংশন বা প্রোপার্টি থাকে, তখন একটি সুন্দর নাম দেওয়ার জন্য সেটিকে সর্বদা @file:JvmName("Foo") দিয়ে অ্যানোটেট করুন।
ডিফল্টরূপে, MyClass.kt ফাইলের শীর্ষ-স্তরের মেম্বারগুলো MyClassKt নামের একটি ক্লাসে অন্তর্ভুক্ত হয়, যা অনাকর্ষণীয় এবং একটি বাস্তবায়নগত খুঁটিনাটি হিসেবে ভাষার পরিচয় ফাঁস করে দেয়।
একাধিক ফাইলের শীর্ষ-স্তরের মেম্বারগুলোকে একটি একক ক্লাসে একত্রিত করতে @file:JvmMultifileClass যোগ করার কথা বিবেচনা করুন।
ল্যাম্বডা আর্গুমেন্ট
জাভাতে সংজ্ঞায়িত সিঙ্গেল মেথড ইন্টারফেস (SAM) ল্যাম্বডা সিনট্যাক্স ব্যবহার করে কোটলিন এবং জাভা উভয় ভাষাতেই প্রয়োগ করা যায়, যা একটি প্রচলিত পদ্ধতিতে ইমপ্লিমেন্টেশনকে ইনলাইন করে। এই ধরনের ইন্টারফেস সংজ্ঞায়িত করার জন্য কোটলিনে বেশ কয়েকটি বিকল্প রয়েছে, যার প্রতিটির মধ্যে সামান্য পার্থক্য আছে।
পছন্দসই সংজ্ঞা
জাভা থেকে ব্যবহারের জন্য তৈরি হায়ার-অর্ডার ফাংশনগুলোর ফাংশন টাইপ Unit রিটার্ন করা উচিত নয়, কারণ সেক্ষেত্রে জাভা কলারদের Unit.INSTANCE রিটার্ন করতে হবে। সিগনেচারে ফাংশন টাইপ ইনলাইন করার পরিবর্তে ফাংশনাল (SAM) ইন্টারফেস ব্যবহার করুন। এছাড়াও, ল্যাম্বডা হিসেবে ব্যবহৃত হবে এমন ইন্টারফেস সংজ্ঞায়িত করার সময় সাধারণ ইন্টারফেসের পরিবর্তে ফাংশনাল (SAM) ইন্টারফেস ব্যবহার করার কথা বিবেচনা করুন, যা কোটলিনে এর প্রচলিত ব্যবহারকে সহজ করে তোলে।
এই কোটলিন সংজ্ঞাটি বিবেচনা করুন:
fun interface GreeterCallback {
fun greetName(String name)
}
fun sayHi(greeter: GreeterCallback) = /* … */
কোটলিন থেকে আহ্বান করা হলে:
sayHi { println("Hello, $it!") }
জাভা থেকে আহ্বান করা হলে:
sayHi(name -> System.out.println("Hello, " + name + "!"));
এমনকি যখন ফাংশন টাইপটি একটি Unit রিটার্ন করে না, তখনও এটিকে একটি নামযুক্ত ইন্টারফেস (named interface) হিসেবে তৈরি করা একটি ভালো ধারণা হতে পারে, যাতে কলাররা এটিকে শুধু ল্যাম্বডার পরিবর্তে একটি নামযুক্ত ক্লাস দিয়েও ইমপ্লিমেন্ট করতে পারে (কোটলিন এবং জাভা উভয় ক্ষেত্রেই)।
class MyGreeterCallback : GreeterCallback {
override fun greetName(name: String) {
println("Hello, $name!");
}
}
যেসব ফাংশন টাইপ Unit রিটার্ন করে, সেগুলো পরিহার করুন।
এই কোটলিন সংজ্ঞাটি বিবেচনা করুন:
fun sayHi(greeter: (String) -> Unit) = /* … */
এর জন্য জাভা কলারদের Unit.INSTANCE রিটার্ন করতে হয়:
sayHi(name -> {
System.out.println("Hello, " + name + "!");
return Unit.INSTANCE;
});
যখন ইমপ্লিমেন্টেশনে স্টেট থাকার কথা থাকে, তখন ফাংশনাল ইন্টারফেস পরিহার করুন।
যখন ইন্টারফেস ইমপ্লিমেন্টেশনের একটি স্টেট থাকার কথা থাকে, তখন ল্যাম্বডা সিনট্যাক্স ব্যবহার করা অর্থহীন। Comparable এর একটি উল্লেখযোগ্য উদাহরণ, কারণ এটির কাজ হলো this other সাথে তুলনা করা, এবং ল্যাম্বডার ' this থাকে না। ইন্টারফেসের আগে fun প্রিফিক্স ব্যবহার না করলে কলার object : ... ` সিনট্যাক্স ব্যবহার করতে বাধ্য হয়, যা এটিকে স্টেট রাখার সুযোগ দেয় এবং কলারকে একটি ইঙ্গিত প্রদান করে।
এই কোটলিন সংজ্ঞাটি বিবেচনা করুন:
// No "fun" prefix.
interface Counter {
fun increment()
}
এটি কোটলিনে ল্যাম্বডা সিনট্যাক্স ব্যবহারে বাধা দেয়, যার জন্য এই দীর্ঘতর সংস্করণটির প্রয়োজন হয়:
runCounter(object : Counter {
private var increments = 0 // State
override fun increment() {
increments++
}
})
জেনেরিক Nothing এড়িয়ে চলুন
যে টাইপের জেনেরিক প্যারামিটার Nothing , তাকে জাভাতে র টাইপ হিসেবে প্রকাশ করা হয়। জাভাতে র টাইপ খুব কম ব্যবহৃত হয় এবং এটি পরিহার করা উচিত।
নথি ব্যতিক্রম
যেসব ফাংশন চেক্ট এক্সেপশন থ্রো করতে পারে, সেগুলোতে @Throws ব্যবহার করে তা ডকুমেন্ট করা উচিত। রানটাইম এক্সেপশনগুলো KDoc-এ ডকুমেন্ট করা উচিত।
একটি ফাংশন যেসব API-এর কাছে কাজ অর্পণ করে, সেগুলোর ব্যাপারে সতর্ক থাকুন, কারণ সেগুলো চেক্ট এক্সেপশন থ্রো করতে পারে, যেগুলোকে কোটলিন সাধারণত নীরবে ছড়িয়ে পড়তে দেয়।
প্রতিরক্ষামূলক অনুলিপি
পাবলিক এপিআই থেকে শেয়ার্ড বা আনওনড রিড-অনলি কালেকশন রিটার্ন করার সময়, সেগুলোকে একটি অপরিবর্তনীয় কন্টেইনারে মুড়ে দিন অথবা একটি ডিফেন্সিভ কপি সম্পাদন করুন। যদিও কোটলিন এগুলোর রিড-অনলি বৈশিষ্ট্য প্রয়োগ করে, জাভার দিকে এমন কোনো প্রয়োগ নেই। র্যাপার বা ডিফেন্সিভ কপি ছাড়া, একটি দীর্ঘস্থায়ী কালেকশন রেফারেন্স রিটার্ন করার মাধ্যমে ইনভেরিয়েন্ট লঙ্ঘিত হতে পারে।
সহযোগী কার্যাবলী
একটি কম্প্যানিয়ন অবজেক্টের পাবলিক ফাংশনগুলোকে স্ট্যাটিক মেথড হিসেবে প্রকাশ করার জন্য, সেগুলোকে অবশ্যই @JvmStatic দিয়ে অ্যানোটেট করতে হবে।
অ্যানোটেশন ছাড়া, এই ফাংশনগুলো শুধুমাত্র একটি স্ট্যাটিক Companion ফিল্ডের ইনস্ট্যান্স মেথড হিসেবে উপলব্ধ থাকে।
ভুল: কোনো টীকা নেই
class KotlinClass {
companion object {
fun doWork() {
/* … */
}
}
}
public final class JavaClass {
public static void main(String... args) {
KotlinClass.Companion.doWork();
}
}
সঠিক: @JvmStatic টীকা
class KotlinClass {
companion object {
@JvmStatic fun doWork() {
/* … */
}
}
}
public final class JavaClass {
public static void main(String... args) {
KotlinClass.doWork();
}
}
সহচর ধ্রুবক
একটি companion object পাবলিক, নন- const প্রোপার্টি, যেগুলো কার্যকরী ধ্রুবক, সেগুলোকে স্ট্যাটিক ফিল্ড হিসেবে প্রকাশ করার জন্য অবশ্যই @JvmField দিয়ে অ্যানোটেট করতে হবে।
অ্যানোটেশনটি ছাড়া, এই প্রোপার্টিগুলো শুধুমাত্র স্ট্যাটিক Companion ফিল্ডের অদ্ভুত নামের ইনস্ট্যান্স 'গেটার' হিসেবেই পাওয়া যায়। @JvmField এর পরিবর্তে @JvmStatic ব্যবহার করলে এই অদ্ভুত নামের 'গেটারগুলো' ক্লাসের স্ট্যাটিক মেথডে চলে যায়, যা এখনও সঠিক নয়।
ভুল: কোনো টীকা নেই
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.Companion.getBIG_INTEGER_ONE());
}
}
ভুল: @JvmStatic টীকা
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
@JvmStatic val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.getBIG_INTEGER_ONE());
}
}
সঠিক: @JvmField অ্যানোটেশন
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
@JvmField val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.BIG_INTEGER_ONE);
}
}
বাগধারামূলক নামকরণ
জাভার থেকে কোটলিনের কলিং কনভেনশন ভিন্ন, যা আপনার ফাংশনের নামকরণের পদ্ধতি পরিবর্তন করতে পারে। @JvmName ব্যবহার করে এমনভাবে নাম ডিজাইন করুন যাতে তা উভয় ভাষার কনভেনশনের জন্য স্বাভাবিক মনে হয় অথবা তাদের নিজ নিজ স্ট্যান্ডার্ড লাইব্রেরির নামকরণের সাথে মেলে।
এটি প্রায়শই এক্সটেনশন ফাংশন এবং এক্সটেনশন প্রপার্টির ক্ষেত্রে ঘটে, কারণ রিসিভার টাইপের অবস্থান ভিন্ন হয়।
sealed class Optional<T : Any>
data class Some<T : Any>(val value: T): Optional<T>()
object None : Optional<Nothing>()
@JvmName("ofNullable")
fun <T> T?.asOptional() = if (this == null) None else Some(this)
// FROM KOTLIN:
fun main(vararg args: String) {
val nullableString: String? = "foo"
val optionalString = nullableString.asOptional()
}
// FROM JAVA:
public static void main(String... args) {
String nullableString = "Foo";
Optional<String> optionalString =
Optionals.ofNullable(nullableString);
}
ডিফল্টগুলির জন্য ফাংশন ওভারলোড
যেসব ফাংশনের প্যারামিটারের ডিফল্ট মান থাকে, সেগুলোতে অবশ্যই @JvmOverloads ব্যবহার করতে হবে। এই অ্যানোটেশনটি ছাড়া কোনো ডিফল্ট মান ব্যবহার করে ফাংশনটি কল করা অসম্ভব।
@JvmOverloads ব্যবহার করার সময়, তৈরি হওয়া মেথডগুলো পরীক্ষা করে দেখুন যে সেগুলো প্রতিটি যৌক্তিক কিনা। যদি তা না হয়, সন্তুষ্ট না হওয়া পর্যন্ত নিম্নলিখিত এক বা উভয় রিফ্যাক্টরিং সম্পাদন করুন:
- প্যারামিটারগুলোর ক্রম পরিবর্তন করে ডিফল্ট মানযুক্ত প্যারামিটারগুলোকে শেষের দিকে রাখুন।
- ডিফল্ট মানগুলোকে ম্যানুয়াল ফাংশন ওভারলোডের অন্তর্ভুক্ত করুন।
ভুল: @JvmOverloads নেই
class Greeting {
fun sayHello(prefix: String = "Mr.", name: String) {
println("Hello, $prefix $name")
}
}
public class JavaClass {
public static void main(String... args) {
Greeting greeting = new Greeting();
greeting.sayHello("Mr.", "Bob");
}
}
সঠিক: @JvmOverloads অ্যানোটেশন।
class Greeting {
@JvmOverloads
fun sayHello(prefix: String = "Mr.", name: String) {
println("Hello, $prefix $name")
}
}
public class JavaClass {
public static void main(String... args) {
Greeting greeting = new Greeting();
greeting.sayHello("Bob");
}
}
লিন্ট চেক
প্রয়োজনীয়তা
- অ্যান্ড্রয়েড স্টুডিও সংস্করণ: 3.2 ক্যানারি 10 বা তার পরবর্তী সংস্করণ
- অ্যান্ড্রয়েড গ্রেডল প্লাগইন সংস্করণ: ৩.২ বা তার পরবর্তী
সমর্থিত চেক
এখন অ্যান্ড্রয়েড লিন্ট চেক রয়েছে যা আপনাকে পূর্বে বর্ণিত কিছু ইন্টারঅপারেবিলিটি সমস্যা সনাক্ত করতে এবং চিহ্নিত করতে সাহায্য করবে। শুধুমাত্র জাভার (কোটলিনের ব্যবহারের জন্য) সমস্যাগুলোই সনাক্ত করা হয়। নির্দিষ্টভাবে, সমর্থিত চেকগুলো হলো:
- অজানা শূন্যতা
- সম্পত্তিতে প্রবেশাধিকার
- কোন কঠিন কোটলিন কীওয়ার্ড নেই
- ল্যাম্বডা প্যারামিটার শেষ
অ্যান্ড্রয়েড স্টুডিও
এই চেকগুলি সক্রিয় করতে, File > Preferences > Editor > Inspections- এ যান এবং Kotlin Interoperability-এর অধীনে আপনি যে নিয়মগুলি সক্রিয় করতে চান সেগুলি চেক করুন:

চিত্র ১. অ্যান্ড্রয়েড স্টুডিওতে কোটলিন আন্তঃকার্যক্ষমতা সেটিংস।
আপনি যে নিয়মগুলো সক্রিয় করতে চান, সেগুলো একবার চেক করে নিলে, আপনার কোড ইন্সপেকশন ( Analyze > Inspect Code… ) চালানোর সময় নতুন চেকগুলোও চলবে।
কমান্ড-লাইন বিল্ড
কমান্ড-লাইন বিল্ড থেকে এই চেকগুলি সক্রিয় করতে, আপনার build.gradle ফাইলে নিম্নলিখিত লাইনটি যোগ করুন:
গ্রুভি
android { ... lintOptions { enable 'Interoperability' } }
কোটলিন
android { ... lintOptions { enable("Interoperability") } }
lintOptions-এর অন্তর্ভুক্ত কনফিগারেশনগুলির সম্পূর্ণ তালিকার জন্য, Android Gradle DSL রেফারেন্স দেখুন।
এরপর, কমান্ড লাইন থেকে ./gradlew lint চালান।