1. आपका स्वागत है!
इस कोडलैब में, आपको अपने कोड को Java से Kotlin में बदलने का तरीका बताया जाएगा. आपको Kotlin लैंग्वेज के नियमों के बारे में भी पता चलेगा. साथ ही, यह भी पता चलेगा कि लिखे जा रहे कोड में इन नियमों का पालन कैसे किया जाए.
यह कोडलैब, Java का इस्तेमाल करने वाले किसी भी डेवलपर के लिए सही है. साथ ही, यह उन डेवलपर के लिए भी सही है जो अपने प्रोजेक्ट को Kotlin पर माइग्रेट करने के बारे में सोच रहे हैं. हम कुछ Java क्लास से शुरुआत करेंगे. इन्हें आईडीई का इस्तेमाल करके Kotlin में बदला जाएगा. इसके बाद, हम बदले गए कोड को देखेंगे और यह देखेंगे कि इसे ज़्यादा मुहावरेदार बनाकर, आम तौर पर होने वाली गलतियों से कैसे बचा जा सकता है.
आपको क्या सीखने को मिलेगा
आपको Java को Kotlin में बदलने का तरीका सीखने को मिलेगा. ऐसा करने से, आपको Kotlin भाषा की इन सुविधाओं और सिद्धांतों के बारे में जानने को मिलेगा:
- शून्यता को मैनेज करना
- सिंगलटन लागू करना
- डेटा क्लास
- स्ट्रिंग मैनेज करना
- एल्विस ऑपरेटर
- डीस्ट्रक्चरिंग
- प्रॉपर्टी और बैकिंग प्रॉपर्टी
- डिफ़ॉल्ट आर्ग्युमेंट और नाम वाले पैरामीटर
- कलेक्शन के साथ काम करना
- एक्सटेंशन फ़ंक्शन
- टॉप-लेवल के फ़ंक्शन और पैरामीटर
let,apply,with, औरrunकीवर्ड
अनुमान
आपको Java के बारे में पहले से जानकारी होनी चाहिए.
आपको किन चीज़ों की ज़रूरत होगी
2. सेट अप करना
नया प्रोजेक्ट बनाना
अगर IntelliJ IDEA का इस्तेमाल किया जा रहा है, तो Kotlin/JVM के साथ एक नया Java प्रोजेक्ट बनाएं.
अगर Android Studio का इस्तेमाल किया जा रहा है, तो No Activity टेंप्लेट का इस्तेमाल करके एक नया प्रोजेक्ट बनाएं. प्रोजेक्ट की भाषा के तौर पर Kotlin चुनें. एसडीके का सबसे पुराना वर्शन कोई भी हो सकता है. इससे नतीजे पर कोई असर नहीं पड़ेगा.
कोड
हम एक User मॉडल ऑब्जेक्ट और एक User सिंगलटन क्लास बनाएंगे. यह क्लास, User ऑब्जेक्ट के साथ काम करती है. साथ ही, यह उपयोगकर्ताओं की सूचियां और फ़ॉर्मैट किए गए उपयोगकर्ता नाम दिखाती है.Repository
app/java/<yourpackagename> में User.java नाम की नई फ़ाइल बनाएं और उसमें यह कोड चिपकाएं:
public class User {
@Nullable
private String firstName;
@Nullable
private String lastName;
public User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
आपको दिखेगा कि आपका आईडीई, @Nullable को परिभाषित नहीं किया गया है. इसलिए, अगर Android Studio का इस्तेमाल किया जा रहा है, तो androidx.annotation.Nullable इंपोर्ट करें. वहीं, अगर IntelliJ का इस्तेमाल किया जा रहा है, तो org.jetbrains.annotations.Nullable इंपोर्ट करें.
Repository.java नाम की एक नई फ़ाइल बनाएं और उसमें यह कोड चिपकाएं:
import java.util.ArrayList;
import java.util.List;
public class Repository {
private static Repository INSTANCE = null;
private List<User> users = null;
public static Repository getInstance() {
if (INSTANCE == null) {
synchronized (Repository.class) {
if (INSTANCE == null) {
INSTANCE = new Repository();
}
}
}
return INSTANCE;
}
// keeping the constructor private to enforce the usage of getInstance
private Repository() {
User user1 = new User("Jane", "");
User user2 = new User("John", null);
User user3 = new User("Anne", "Doe");
users = new ArrayList();
users.add(user1);
users.add(user2);
users.add(user3);
}
public List<User> getUsers() {
return users;
}
public List<String> getFormattedUserNames() {
List<String> userNames = new ArrayList<>(users.size());
for (User user : users) {
String name;
if (user.getLastName() != null) {
if (user.getFirstName() != null) {
name = user.getFirstName() + " " + user.getLastName();
} else {
name = user.getLastName();
}
} else if (user.getFirstName() != null) {
name = user.getFirstName();
} else {
name = "Unknown";
}
userNames.add(name);
}
return userNames;
}
}
3. शून्य होने की स्थिति, val, var, और डेटा क्लास के बारे में एलान करना
हमारा IDE, Java कोड को Kotlin कोड में अपने-आप बदलने का काम बहुत अच्छी तरह से कर सकता है. हालांकि, कभी-कभी इसे थोड़ी मदद की ज़रूरत होती है. आइए, अपने आईडीई को कन्वर्ज़न का शुरुआती पास करने दें. इसके बाद, हम जनरेट किए गए कोड की जांच करेंगे. इससे हमें यह समझने में मदद मिलेगी कि इसे इस तरह से क्यों और कैसे बदला गया है.
User.java फ़ाइल पर जाएं और उसे Kotlin में बदलें: मेन्यू बार -> कोड -> Java फ़ाइल को Kotlin फ़ाइल में बदलें.
अगर कन्वर्ज़न के बाद आपका आईडीई सुधार करने के लिए कहता है, तो हां दबाएं.

आपको यह Kotlin कोड दिखेगा:
class User(var firstName: String?, var lastName: String?)
ध्यान दें कि User.java का नाम बदलकर User.kt कर दिया गया है. Kotlin फ़ाइलों का एक्सटेंशन .kt होता है.
हमारी Java User क्लास में दो प्रॉपर्टी थीं: firstName और lastName. इनमें से हर एक में गेटर और सेटर मेथड होता है, जिससे इसकी वैल्यू में बदलाव किया जा सकता है. बदली जा सकने वाली वैरिएबल के लिए Kotlin का कीवर्ड var है. इसलिए, कनवर्टर इन प्रॉपर्टी में से हर एक के लिए var का इस्तेमाल करता है. अगर हमारी Java प्रॉपर्टी में सिर्फ़ गेटर होते, तो उन्हें सिर्फ़ पढ़ा जा सकता था और उन्हें val वैरिएबल के तौर पर एलान किया जाता. val, Java में final कीवर्ड की तरह होता है.
Kotlin और Java के बीच एक मुख्य अंतर यह है कि Kotlin में यह साफ़ तौर पर बताया जाता है कि कोई वैरिएबल, शून्य वैल्यू स्वीकार कर सकता है या नहीं. इसके लिए, टाइप डिक्लेरेशन में ? जोड़ा जाता है.
हमने firstName और lastName को नल के तौर पर मार्क किया है. इसलिए, ऑटो-कन्वर्टर ने String? के साथ प्रॉपर्टी को अपने-आप नल के तौर पर मार्क कर दिया. अगर आपने अपने Java सदस्यों को गैर-शून्य के तौर पर एनोटेट किया है (org.jetbrains.annotations.NotNull या androidx.annotation.NonNull का इस्तेमाल करके), तो कनवर्टर इसे पहचान लेगा. साथ ही, Kotlin में भी फ़ील्ड को गैर-शून्य बना देगा.
बेसिक कन्वर्ज़न पहले ही हो चुका है. हालांकि, इसे मुहावरेदार तरीके से लिखा जा सकता है. आइए, देखें कि कैसे.
डेटा क्लास
हमारी User क्लास में सिर्फ़ डेटा होता है. Kotlin में, इस भूमिका वाली क्लास के लिए एक कीवर्ड होता है: data. इस क्लास को data के तौर पर मार्क करने पर, कंपाइलर हमारे लिए अपने-आप गैटर और सेटर बना देगा. इससे equals(), hashCode(), और toString() फ़ंक्शन भी मिलेंगे.
आइए, User क्लास में data कीवर्ड जोड़ते हैं:
data class User(var firstName: String?, var lastName: String?)
Java की तरह Kotlin में भी एक मुख्य कंस्ट्रक्टर और एक या उससे ज़्यादा सेकंडरी कंस्ट्रक्टर हो सकते हैं. ऊपर दिए गए उदाहरण में, User क्लास का मुख्य कंस्ट्रक्टर है. अगर आपको एक ऐसी Java क्लास को Kotlin में बदलना है जिसमें कई कंस्ट्रक्टर हैं, तो कन्वर्टर Kotlin में भी अपने-आप कई कंस्ट्रक्टर बना देगा. इन्हें constructor कीवर्ड का इस्तेमाल करके तय किया जाता है.
अगर हमें इस क्लास का इंस्टेंस बनाना है, तो हम इसे इस तरह बना सकते हैं:
val user1 = User("Jane", "Doe")
समानता
Kotlin में दो तरह की समानता होती है:
- स्ट्रक्चरल समानता के लिए,
==ऑपरेटर का इस्तेमाल किया जाता है. साथ ही, यह तय करने के लिए कि दो इंस्टेंस बराबर हैं या नहीं,equals()को कॉल किया जाता है. - रेफ़रेंशियल समानता के लिए
===ऑपरेटर का इस्तेमाल किया जाता है. इससे यह पता चलता है कि दो रेफ़रंस एक ही ऑब्जेक्ट की ओर इशारा करते हैं या नहीं.
डेटा क्लास के प्राइमरी कंस्ट्रक्टर में तय की गई प्रॉपर्टी का इस्तेमाल, स्ट्रक्चरल समानता की जांच के लिए किया जाएगा.
val user1 = User("Jane", "Doe")
val user2 = User("Jane", "Doe")
val structurallyEqual = user1 == user2 // true
val referentiallyEqual = user1 === user2 // false
4. डिफ़ॉल्ट आर्ग्युमेंट, नाम वाले आर्ग्युमेंट
Kotlin में, फ़ंक्शन कॉल में मौजूद आर्ग्युमेंट को डिफ़ॉल्ट वैल्यू असाइन की जा सकती हैं. आर्ग्युमेंट को शामिल न करने पर, डिफ़ॉल्ट वैल्यू का इस्तेमाल किया जाता है. Kotlin में, कंस्ट्रक्टर भी फ़ंक्शन होते हैं. इसलिए, हम डिफ़ॉल्ट आर्ग्युमेंट का इस्तेमाल करके यह तय कर सकते हैं कि lastName की डिफ़ॉल्ट वैल्यू null है. इसके लिए, हम null को lastName पर असाइन करते हैं.
data class User(var firstName: String?, var lastName: String? = null)
// usage
val jane = User("Jane") // same as User("Jane", null)
val joe = User("Joe", "Doe")
Kotlin में, फ़ंक्शन कॉल करते समय आर्ग्युमेंट को लेबल करने की सुविधा मिलती है:
val john = User(firstName = "John", lastName = "Doe")
एक अन्य उदाहरण के तौर पर, मान लें कि firstName की डिफ़ॉल्ट वैल्यू null है और lastName की कोई डिफ़ॉल्ट वैल्यू नहीं है. इस मामले में, डिफ़ॉल्ट पैरामीटर, बिना डिफ़ॉल्ट वैल्यू वाले पैरामीटर से पहले आएगा. इसलिए, आपको नाम वाले आर्ग्युमेंट के साथ फ़ंक्शन को कॉल करना होगा:
data class User(var firstName: String? = null, var lastName: String?)
// usage
val jane = User(lastName = "Doe") // same as User(null, "Doe")
val john = User("John", "Doe")
डिफ़ॉल्ट वैल्यू, Kotlin कोड में इस्तेमाल किया जाने वाला एक ज़रूरी कॉन्सेप्ट है. हमारे कोडलैब में, हम User ऑब्जेक्ट डिक्लेरेशन में हमेशा पहला और आखिरी नाम तय करना चाहते हैं. इसलिए, हमें डिफ़ॉल्ट वैल्यू की ज़रूरत नहीं है.
5. ऑब्जेक्ट को शुरू करना, कंपैनियन ऑब्जेक्ट, और सिंगलटन
कोडलैब जारी रखने से पहले, पक्का करें कि आपकी User क्लास, data क्लास हो. अब, Repository क्लास को Kotlin में बदलते हैं. ऑटोमैटिक कन्वर्ज़न का नतीजा ऐसा दिखना चाहिए:
import java.util.*
class Repository private constructor() {
private var users: MutableList<User?>? = null
fun getUsers(): List<User?>? {
return users
}
val formattedUserNames: List<String?>
get() {
val userNames: MutableList<String?> =
ArrayList(users!!.size)
for (user in users) {
var name: String
name = if (user!!.lastName != null) {
if (user!!.firstName != null) {
user!!.firstName + " " + user!!.lastName
} else {
user!!.lastName
}
} else if (user!!.firstName != null) {
user!!.firstName
} else {
"Unknown"
}
userNames.add(name)
}
return userNames
}
companion object {
private var INSTANCE: Repository? = null
val instance: Repository?
get() {
if (INSTANCE == null) {
synchronized(Repository::class.java) {
if (INSTANCE == null) {
INSTANCE =
Repository()
}
}
}
return INSTANCE
}
}
// keeping the constructor private to enforce the usage of getInstance
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList<Any?>()
users.add(user1)
users.add(user2)
users.add(user3)
}
}
आइए, देखते हैं कि ऑटोमैटिक कन्वर्ज़न टूल ने क्या किया:
usersकी सूची में नल वैल्यू हो सकती है, क्योंकि ऑब्जेक्ट को डिक्लेरेशन के समय इंस्टैंटिएट नहीं किया गया था- Kotlin में
getUsers()जैसे फ़ंक्शन,funमॉडिफ़ायर के साथ एलान किए जाते हैं getFormattedUserNames()तरीका अबformattedUserNamesनाम की प्रॉपर्टी है- उपयोगकर्ताओं की सूची (जो शुरू में
getFormattedUserNames(का हिस्सा थी) पर दोहराव करने का सिंटैक्स, Java के सिंटैक्स से अलग होता है staticफ़ील्ड अबcompanion objectब्लॉक का हिस्सा हैinitब्लॉक जोड़ा गया
आगे बढ़ने से पहले, आइए कोड को थोड़ा सा ठीक कर लें. कंस्ट्रक्टर में देखने पर, हमें पता चलता है कि कनवर्टर ने हमारी users लिस्ट को एक ऐसी लिस्ट बना दिया है जिसमें बदलाव किया जा सकता है. साथ ही, इसमें ऐसे ऑब्जेक्ट शामिल हैं जो नल हो सकते हैं. हालांकि, सूची में कोई भी उपयोगकर्ता नहीं हो सकता, लेकिन मान लेते हैं कि इसमें कोई भी उपयोगकर्ता नहीं है. इसलिए, यह तरीका अपनाएं:
usersटाइप के एलान में,User?के अंदर मौजूद?को हटाएंgetUsers()के लिए,User?में मौजूद?को हटाएं, ताकिList<User>?दिखे
Init block
Kotlin में, मुख्य कंस्ट्रक्टर में कोई कोड नहीं हो सकता. इसलिए, शुरुआती कोड को init ब्लॉक में रखा जाता है. दोनों में एक जैसी सुविधाएं हैं.
class Repository private constructor() {
...
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList<Any?>()
users.add(user1)
users.add(user2)
users.add(user3)
}
}
init कोड का ज़्यादातर हिस्सा, प्रॉपर्टी को शुरू करने का काम करता है. इसे प्रॉपर्टी के एलान में भी किया जा सकता है. उदाहरण के लिए, हमारी Repository क्लास के Kotlin वर्शन में, हम देखते हैं कि users प्रॉपर्टी को डिक्लेरेशन में शुरू किया गया था.
private var users: MutableList<User>? = null
Kotlin की static प्रॉपर्टी और मेथड
Java में, हम फ़ील्ड या फ़ंक्शन के लिए static कीवर्ड का इस्तेमाल करते हैं. इससे यह पता चलता है कि वे किसी क्लास से जुड़े हैं, लेकिन क्लास के किसी इंस्टेंस से नहीं. इसलिए, हमने अपनी Repository क्लास में INSTANCE स्टैटिक फ़ील्ड बनाया है. इसके लिए, Kotlin में companion object ब्लॉक का इस्तेमाल किया जाता है. यहां स्टैटिक फ़ील्ड और स्टैटिक फ़ंक्शन भी घोषित किए जाते हैं. कन्वर्टर ने कंपैनियन ऑब्जेक्ट ब्लॉक बनाया और INSTANCE फ़ील्ड को यहां ले गया.
सिंगलटन को मैनेज करना
हमें Repository क्लास का सिर्फ़ एक इंस्टेंस चाहिए. इसलिए, हमने Java में सिंगलटन पैटर्न का इस्तेमाल किया है. Kotlin में, कंपाइलर लेवल पर इस पैटर्न को लागू किया जा सकता है. इसके लिए, class कीवर्ड को object से बदलें.
प्राइवेट कंस्ट्रक्टर को हटाएं और क्लास की परिभाषा को object Repository से बदलें. साथ में दिखने वाले ऑब्जेक्ट को भी हटाएं.
object Repository {
private var users: MutableList<User>? = null
fun getUsers(): List<User>? {
return users
}
val formattedUserNames: List<String>
get() {
val userNames: MutableList<String> =
ArrayList(users!!.size)
for (user in users) {
var name: String
name = if (user!!.lastName != null) {
if (user!!.firstName != null) {
user!!.firstName + " " + user!!.lastName
} else {
user!!.lastName
}
} else if (user!!.firstName != null) {
user!!.firstName
} else {
"Unknown"
}
userNames.add(name)
}
return userNames
}
// keeping the constructor private to enforce the usage of getInstance
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users = ArrayList<Any?>()
users.add(user1)
users.add(user2)
users.add(user3)
}
}
object क्लास का इस्तेमाल करते समय, हम ऑब्जेक्ट पर सीधे तौर पर फ़ंक्शन और प्रॉपर्टी को कॉल करते हैं. जैसे:
val formattedUserNames = Repository.formattedUserNames
ध्यान दें कि अगर किसी प्रॉपर्टी के लिए विज़िबिलिटी मॉडिफ़ायर सेट नहीं किया गया है, तो वह डिफ़ॉल्ट रूप से सार्वजनिक होती है. जैसे, Repository ऑब्जेक्ट में मौजूद formattedUserNames प्रॉपर्टी.
6. शून्यता को मैनेज करना
Repository क्लास को Kotlin में बदलते समय, ऑटोमैटिक कन्वर्टर ने उपयोगकर्ताओं की सूची को शून्य के तौर पर सेट किया. ऐसा इसलिए हुआ, क्योंकि इसे ऑब्जेक्ट के तौर पर तब शुरू नहीं किया गया था, जब इसे घोषित किया गया था. इस वजह से, users ऑब्जेक्ट के सभी इस्तेमाल के लिए, not-null assertion ऑपरेटर !! का इस्तेमाल करना ज़रूरी है. (आपको पूरे बदले गए कोड में users!! और user!! दिखेगा.) !! ऑपरेटर, किसी भी वैरिएबल को नॉन-नल टाइप में बदलता है, ताकि उस पर प्रॉपर्टी ऐक्सेस की जा सकें या फ़ंक्शन कॉल किए जा सकें. हालांकि, अगर वैरिएबल की वैल्यू वाकई में शून्य है, तो एक अपवाद दिखेगा. !! का इस्तेमाल करने पर, रनटाइम के दौरान अपवादों के होने का जोखिम होता है.
इसके बजाय, इनमें से किसी एक तरीके का इस्तेमाल करके, नल वैल्यू को मैनेज करें:
- शून्य की जांच करना (
if (users != null) {...}) - एल्विस ऑपरेटर
?:का इस्तेमाल करके (इसके बारे में कोडलैब में बाद में बताया गया है) - Kotlin के कुछ स्टैंडर्ड फ़ंक्शन का इस्तेमाल करना. इनके बारे में, इस कोडलैब में बाद में बताया गया है
हमारे मामले में, हमें पता है कि उपयोगकर्ताओं की सूची को नल नहीं किया जाना चाहिए, क्योंकि ऑब्जेक्ट बनने के तुरंत बाद (init ब्लॉक में) इसे शुरू किया जाता है. इसलिए, हम users ऑब्जेक्ट को सीधे तौर पर तब इंस्टैंशिएट कर सकते हैं, जब हम इसका एलान करते हैं.
कलेक्शन टाइप के इंस्टेंस बनाते समय, Kotlin कई हेल्पर फ़ंक्शन उपलब्ध कराता है. इनसे आपके कोड को ज़्यादा आसानी से समझा जा सकता है और उसे ज़्यादा फ़्लेक्सिबल बनाया जा सकता है. यहां users के लिए MutableList का इस्तेमाल किया जा रहा है:
private var users: MutableList<User>? = null
आसानी के लिए, हम mutableListOf() फ़ंक्शन का इस्तेमाल कर सकते हैं और सूची के एलिमेंट का टाइप दे सकते हैं. mutableListOf<User>() एक खाली सूची बनाता है, जिसमें User ऑब्जेक्ट शामिल किए जा सकते हैं. अब कंपाइलर, वैरिएबल के डेटा टाइप का अनुमान लगा सकता है. इसलिए, users प्रॉपर्टी के टाइप का एलान हटा दें.
private val users = mutableListOf<User>()
हमने var को val में भी बदल दिया है, क्योंकि उपयोगकर्ताओं के पास उपयोगकर्ताओं की सूची का सिर्फ़ पढ़ने वाला रेफ़रंस होगा. ध्यान दें कि रेफ़रंस सिर्फ़ पढ़ने के लिए होता है. इसलिए, यह कभी भी नई सूची की ओर नहीं ले जा सकता. हालांकि, सूची में बदलाव किया जा सकता है. इसमें एलिमेंट जोड़े या हटाए जा सकते हैं.
users वैरिएबल पहले से ही शुरू किया जा चुका है. इसलिए, init ब्लॉक से इस वैरिएबल को शुरू करने की सुविधा हटाएं:
users = ArrayList<Any?>()
इसके बाद, init ब्लॉक ऐसा दिखेगा:
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
users.add(user1)
users.add(user2)
users.add(user3)
}
इन बदलावों के बाद, हमारी users प्रॉपर्टी अब नॉन-नल है. साथ ही, हम !! ऑपरेटर के सभी गैर-ज़रूरी उदाहरणों को हटा सकते हैं. ध्यान दें कि आपको Android Studio में अब भी कंपाइल करने से जुड़ी गड़बड़ियां दिखेंगी. हालांकि, इन्हें ठीक करने के लिए, कोडलैब के अगले कुछ चरणों को पूरा करें.
val userNames: MutableList<String?> = ArrayList(users.size)
for (user in users) {
var name: String
name = if (user.lastName != null) {
if (user.firstName != null) {
user.firstName + " " + user.lastName
} else {
user.lastName
}
} else if (user.firstName != null) {
user.firstName
} else {
"Unknown"
}
userNames.add(name)
}
इसके अलावा, अगर userNames वैल्यू के लिए, ArrayList के टाइप को Strings के तौर पर तय किया जाता है, तो एलान में टाइप को हटाया जा सकता है. ऐसा इसलिए, क्योंकि इसका अनुमान लगाया जाएगा.
val userNames = ArrayList<String>(users.size)
Destructuring
Kotlin में, किसी ऑब्जेक्ट को कई वैरिएबल में बांटा जा सकता है. इसके लिए, डिस्ट्रक्चरिंग डिक्लेरेशन नाम के सिंटैक्स का इस्तेमाल किया जाता है. हम कई वैरिएबल बनाते हैं और उनका इस्तेमाल अलग-अलग किया जा सकता है.
उदाहरण के लिए, data क्लास में डिस्ट्रक्चरिंग की सुविधा होती है. इसलिए, हम for लूप में User ऑब्जेक्ट को (firstName, lastName) में डिस्ट्रक्चर कर सकते हैं. इससे हमें firstName और lastName वैल्यू के साथ सीधे तौर पर काम करने की अनुमति मिलती है. नीचे दिखाए गए तरीके से for लूप को अपडेट करें. user.firstName के सभी इंस्टेंस को firstName से बदलें और user.lastName को lastName से बदलें.
for ((firstName, lastName) in users) {
var name: String
name = if (lastName != null) {
if (firstName != null) {
firstName + " " + lastName
} else {
lastName
}
} else if (firstName != null) {
firstName
} else {
"Unknown"
}
userNames.add(name)
}
if एक्सप्रेशन
userName की सूची में दिए गए नाम, अब भी हमारे तय किए गए फ़ॉर्मैट में नहीं हैं. lastName और firstName, दोनों null हो सकते हैं. इसलिए, फ़ॉर्मैट किए गए उपयोगकर्ता नामों की सूची बनाते समय, हमें nullability को मैनेज करना होगा. अगर दोनों में से कोई भी नाम मौजूद नहीं है, तो हमें "Unknown" दिखाना है. name वैरिएबल को एक बार सेट करने के बाद बदला नहीं जा सकता. इसलिए, हम var के बजाय val का इस्तेमाल कर सकते हैं. पहले यह बदलाव करें.
val name: String
नाम वैरिएबल सेट करने वाले कोड को देखें. आपको यह नया लग सकता है कि किसी वैरिएबल को if / else ब्लॉक के कोड के बराबर सेट किया जा रहा है. ऐसा इसलिए किया जा सकता है, क्योंकि Kotlin में if और when एक्सप्रेशन हैं. ये वैल्यू को रिटर्न करते हैं. if स्टेटमेंट की आखिरी लाइन को name असाइन किया जाएगा. इस ब्लॉक का मकसद सिर्फ़ name वैल्यू को शुरू करना है.
असल में, यहां दिया गया लॉजिक यह है कि अगर lastName की वैल्यू शून्य है, तो name की वैल्यू firstName या "Unknown" पर सेट होती है.
name = if (lastName != null) {
if (firstName != null) {
firstName + " " + lastName
} else {
lastName
}
} else if (firstName != null) {
firstName
} else {
"Unknown"
}
एल्विस ऑपरेटर
इस कोड को एल्विस ऑपरेटर ?: का इस्तेमाल करके, ज़्यादा मुहावरेदार तरीके से लिखा जा सकता है. अगर बाईं ओर का एक्सप्रेशन शून्य नहीं है, तो एल्विस ऑपरेटर बाईं ओर का एक्सप्रेशन दिखाएगा. अगर बाईं ओर का एक्सप्रेशन शून्य है, तो वह दाईं ओर का एक्सप्रेशन दिखाएगा.
इसलिए, यहां दिए गए कोड में, अगर firstName शून्य नहीं है, तो इसे दिखाया जाता है. अगर firstName शून्य है, तो एक्सप्रेशन , दाईं ओर मौजूद वैल्यू "Unknown" दिखाता है:
name = if (lastName != null) {
...
} else {
firstName ?: "Unknown"
}
7. स्ट्रिंग टेंप्लेट
Kotlin में String के साथ काम करना आसान होता है. इसके लिए, स्ट्रिंग टेंप्लेट का इस्तेमाल किया जाता है. स्ट्रिंग टेंप्लेट की मदद से, स्ट्रिंग के अंदर किए गए एलान के वैरिएबल का रेफ़रंस लिया जा सकता है. इसके लिए, वैरिएबल से पहले $ सिंबल का इस्तेमाल करें. स्ट्रिंग के एलान में भी एक्सप्रेशन डाला जा सकता है. इसके लिए, एक्सप्रेशन को { } में रखें और उससे पहले $ सिंबल का इस्तेमाल करें. उदाहरण: ${user.firstName}.
फ़िलहाल, आपका कोड स्ट्रिंग कॉनकैटेनेशन का इस्तेमाल करके, firstName और lastName को उपयोगकर्ता नाम में जोड़ता है.
if (firstName != null) {
firstName + " " + lastName
}
इसके बजाय, स्ट्रिंग को इस तरह जोड़ें:
if (firstName != null) {
"$firstName $lastName"
}
स्ट्रिंग टेंप्लेट का इस्तेमाल करके, अपने कोड को आसान बनाया जा सकता है.
अगर आपके कोड को लिखने का कोई और बेहतर तरीका है, तो आपका आईडीई आपको चेतावनियां दिखाएगा. आपको कोड में एक घुमावदार अंडरलाइन दिखेगी. इस पर कर्सर घुमाने पर, आपको कोड को फिर से व्यवस्थित करने का सुझाव दिखेगा.
फ़िलहाल, आपको एक चेतावनी दिखेगी कि name डिक्लेरेशन को असाइनमेंट के साथ जोड़ा जा सकता है. चलिए, इसे लागू करते हैं. name वैरिएबल के टाइप का पता लगाया जा सकता है. इसलिए, हम String टाइप के बारे में साफ़ तौर पर दी गई जानकारी को हटा सकते हैं. अब हमारा formattedUserNames ऐसा दिखता है:
val formattedUserNames: List<String?>
get() {
val userNames = ArrayList<String>(users.size)
for ((firstName, lastName) in users) {
val name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName
}
} else {
firstName ?: "Unknown"
}
userNames.add(name)
}
return userNames
}
हम एक और बदलाव कर सकते हैं. अगर पहला और आखिरी नाम मौजूद नहीं है, तो हमारा यूज़र इंटरफ़ेस (यूआई) लॉजिक "Unknown" दिखाता है. इसलिए, हम शून्य ऑब्जेक्ट का इस्तेमाल नहीं करते. इसलिए, formattedUserNames के डेटा टाइप के लिए, List<String?> को List<String> से बदलें.
val formattedUserNames: List<String>
8. कलेक्शन पर की जाने वाली कार्रवाइयां
आइए, formattedUserNames getter पर करीब से नज़र डालें और देखें कि इसे और ज़्यादा मुहावरेदार कैसे बनाया जा सकता है. फ़िलहाल, यह कोड ये काम करता है:
- यह फ़ंक्शन, स्ट्रिंग की नई सूची बनाता है
- यह कुकी, उपयोगकर्ताओं की सूची में बदलाव करती है
- यह कुकी, उपयोगकर्ता के नाम और उपनाम के आधार पर, हर उपयोगकर्ता के लिए फ़ॉर्मैट किया गया नाम बनाती है
- इससे नई बनाई गई सूची दिखती है
val formattedUserNames: List<String>
get() {
val userNames = ArrayList<String>(users.size)
for ((firstName, lastName) in users) {
val name = if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName
}
} else {
firstName ?: "Unknown"
}
userNames.add(name)
}
return userNames
}
Kotlin, कलेक्शन ट्रांसफ़ॉर्मेशन की एक लंबी सूची उपलब्ध कराता है. इससे Java Collections API की क्षमताओं को बढ़ाकर, डेवलपमेंट को तेज़ और सुरक्षित बनाया जा सकता है. इनमें से एक फ़ंक्शन map है. यह फ़ंक्शन एक नई सूची दिखाता है. इसमें, ओरिजनल सूची के हर एलिमेंट पर दिए गए ट्रांसफ़ॉर्म फ़ंक्शन को लागू करने के बाद मिले नतीजे शामिल होते हैं. इसलिए, नई सूची बनाने और उपयोगकर्ताओं की सूची को मैन्युअल तरीके से दोहराने के बजाय, हम map फ़ंक्शन का इस्तेमाल कर सकते हैं. साथ ही, for लूप में मौजूद लॉजिक को map बॉडी में ले जा सकते हैं. डिफ़ॉल्ट रूप से, map में इस्तेमाल किए गए मौजूदा सूची आइटम का नाम it होता है. हालांकि, पढ़ने में आसानी के लिए, it को अपने वैरिएबल के नाम से बदला जा सकता है. हमारे मामले में, आइए इसे user नाम दें:
val formattedUserNames: List<String>
get() {
return users.map { user ->
val name = if (user.lastName != null) {
if (user.firstName != null) {
"${user.firstName} ${user.lastName}"
} else {
user.lastName ?: "Unknown"
}
} else {
user.firstName ?: "Unknown"
}
name
}
}
ध्यान दें कि अगर user.lastName शून्य है, तो हम Elvis ऑपरेटर का इस्तेमाल करके "Unknown" को वापस लाते हैं. ऐसा इसलिए, क्योंकि user.lastName, String? टाइप का है और name के लिए String की ज़रूरत होती है.
...
else {
user.lastName ?: "Unknown"
}
...
इसे और भी आसान बनाने के लिए, हम name वैरिएबल को पूरी तरह से हटा सकते हैं:
val formattedUserNames: List<String>
get() {
return users.map { user ->
if (user.lastName != null) {
if (user.firstName != null) {
"${user.firstName} ${user.lastName}"
} else {
user.lastName ?: "Unknown"
}
} else {
user.firstName ?: "Unknown"
}
}
}
9. प्रॉपर्टी और बैकिंग प्रॉपर्टी
हमने देखा कि ऑटोमैटिक कन्वर्टर ने getFormattedUserNames() फ़ंक्शन को formattedUserNames नाम की प्रॉपर्टी से बदल दिया है. इसमें कस्टम गेटर होता है. हालांकि, Kotlin अब भी getFormattedUserNames() मेथड जनरेट करता है, जो List दिखाता है.
Java में, हम अपनी क्लास की प्रॉपर्टी को getter और setter फ़ंक्शन के ज़रिए ऐक्सेस करते हैं. Kotlin हमें क्लास की प्रॉपर्टी (जिन्हें फ़ील्ड के तौर पर दिखाया जाता है) और फ़ंक्शनैलिटी (जिन्हें फ़ंक्शन के तौर पर दिखाया जाता है) के बीच बेहतर अंतर करने की सुविधा देता है. फ़ंक्शनैलिटी, वे कार्रवाइयां होती हैं जो कोई क्लास कर सकती है. हमारे मामले में, Repository क्लास बहुत आसान है और इसमें कोई कार्रवाई नहीं की जाती है. इसलिए, इसमें सिर्फ़ फ़ील्ड होते हैं.
Java के getFormattedUserNames() फ़ंक्शन में ट्रिगर होने वाला लॉजिक अब formattedUserNames Kotlin प्रॉपर्टी के गेटर को कॉल करने पर ट्रिगर होता है.
हालांकि, हमारे पास formattedUserNames प्रॉपर्टी से मेल खाने वाला कोई फ़ील्ड नहीं है, लेकिन Kotlin हमें field नाम का एक अपने-आप तैयार होने वाला बैकिंग फ़ील्ड उपलब्ध कराता है. अगर ज़रूरत हो, तो हम इसे कस्टम गेटर और सेटर से ऐक्सेस कर सकते हैं.
हालांकि, कभी-कभी हमें कुछ ऐसी अतिरिक्त सुविधाओं की ज़रूरत होती है जो अपने-आप तैयार होने वाले बैकिंग फ़ील्ड में नहीं मिलती हैं.
आइए, एक उदाहरण से इसे समझें.
हमारी Repository क्लास में, उपयोगकर्ताओं की एक ऐसी सूची है जिसमें बदलाव किया जा सकता है. इसे getUsers() फ़ंक्शन में दिखाया जा रहा है. यह फ़ंक्शन, हमारे Java कोड से जनरेट हुआ था:
fun getUsers(): List<User>? {
return users
}
हम नहीं चाहते थे कि Repository क्लास के कॉलर, उपयोगकर्ताओं की सूची में बदलाव करें. इसलिए, हमने getUsers() फ़ंक्शन बनाया है, जो सिर्फ़ पढ़ने की अनुमति वाला List<User> दिखाता है. ऐसे मामलों के लिए, हम Kotlin में फ़ंक्शन के बजाय प्रॉपर्टी का इस्तेमाल करना पसंद करते हैं. ज़्यादा सटीक तरीके से कहें, तो हम सिर्फ़ पढ़ने के लिए List<User> को दिखाएंगे, जो mutableListOf<User> पर आधारित है.
सबसे पहले, users का नाम बदलकर _users कर देते हैं. वैरिएबल के नाम को हाइलाइट करें. इसके बाद, वैरिएबल का नाम बदलने के लिए, राइट क्लिक करके रीफ़ैक्टर > नाम बदलें पर क्लिक करें. इसके बाद, सिर्फ़ पढ़ने की अनुमति वाली कोई ऐसी सार्वजनिक प्रॉपर्टी जोड़ें जो उपयोगकर्ताओं की सूची दिखाती हो. इसे users कहते हैं:
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
इस चरण पर, getUsers() तरीके को मिटाया जा सकता है.
ऊपर दिए गए बदलाव के बाद, निजी _users प्रॉपर्टी, सार्वजनिक users प्रॉपर्टी के लिए बैकअप प्रॉपर्टी बन जाती है. Repository क्लास के बाहर, _users सूची में बदलाव नहीं किया जा सकता. ऐसा इसलिए, क्योंकि क्लास के उपभोक्ता सिर्फ़ users के ज़रिए सूची को ऐक्सेस कर सकते हैं.
जब Kotlin कोड से users को कॉल किया जाता है, तब Kotlin स्टैंडर्ड लाइब्रेरी से List को लागू किया जाता है. इसमें सूची में बदलाव नहीं किया जा सकता. अगर Java से users को कॉल किया जाता है, तो java.util.List का इस्तेमाल किया जाता है. इसमें सूची में बदलाव किया जा सकता है. साथ ही, add() और remove() जैसे ऑपरेशन उपलब्ध होते हैं.
पूरा कोड:
object Repository {
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
val formattedUserNames: List<String>
get() {
return _users.map { user ->
if (user.lastName != null) {
if (user.firstName != null) {
"${user.firstName} ${user.lastName}"
} else {
user.lastName ?: "Unknown"
}
} else {
user.firstName ?: "Unknown"
}
}
}
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
_users.add(user1)
_users.add(user2)
_users.add(user3)
}
}
10. टॉप-लेवल और एक्सटेंशन फ़ंक्शन और प्रॉपर्टी
फ़िलहाल, Repository क्लास को यह पता है कि User ऑब्जेक्ट के लिए, फ़ॉर्मैट किया गया उपयोगकर्ता का नाम कैसे कैलकुलेट किया जाता है. हालांकि, अगर हमें फ़ॉर्मैटिंग के इसी लॉजिक का इस्तेमाल दूसरी क्लास में करना है, तो हमें इसे कॉपी करके चिपकाना होगा या User क्लास में ले जाना होगा.
Kotlin में, किसी भी क्लास, ऑब्जेक्ट या इंटरफ़ेस के बाहर फ़ंक्शन और प्रॉपर्टी का एलान करने की सुविधा मिलती है. उदाहरण के लिए, mutableListOf() फ़ंक्शन का इस्तेमाल करके, List का नया इंस्टेंस बनाया जाता है. यह फ़ंक्शन, Kotlin स्टैंडर्ड लाइब्रेरी के Collections.kt में पहले से ही तय किया गया है.
Java में, जब भी आपको कुछ यूटिलिटी फ़ंक्शन की ज़रूरत होती है, तब ज़्यादातर मामलों में Util क्लास बनाई जाती है. साथ ही, उस फ़ंक्शन को स्टैटिक फ़ंक्शन के तौर पर एलान किया जाता है. Kotlin में, क्लास के बिना टॉप-लेवल फ़ंक्शन का एलान किया जा सकता है. हालांकि, Kotlin में एक्सटेंशन फ़ंक्शन बनाने की सुविधा भी मिलती है. ये ऐसे फ़ंक्शन होते हैं जो किसी टाइप को बढ़ाते हैं, लेकिन टाइप के बाहर घोषित किए जाते हैं.
विज़िबिलिटी मॉडिफ़ायर का इस्तेमाल करके, एक्सटेंशन फ़ंक्शन और प्रॉपर्टी की विज़िबिलिटी को सीमित किया जा सकता है. इनसे, एक्सटेंशन का इस्तेमाल सिर्फ़ उन क्लास तक सीमित रहता है जिन्हें इनकी ज़रूरत होती है. साथ ही, ये नेमस्पेस को खराब नहीं करते.
User क्लास के लिए, हम फ़ॉर्मैट किया गया नाम कंप्यूट करने वाला एक्सटेंशन फ़ंक्शन जोड़ सकते हैं. इसके अलावा, हम फ़ॉर्मैट किए गए नाम को एक्सटेंशन प्रॉपर्टी में सेव कर सकते हैं. इसे Repository क्लास के बाहर, उसी फ़ाइल में जोड़ा जा सकता है:
// extension function
fun User.getFormattedName(): String {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
// extension property
val User.userFormattedName: String
get() {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
// usage:
val user = User(...)
val name = user.getFormattedName()
val formattedName = user.userFormattedName
इसके बाद, हम एक्सटेंशन फ़ंक्शन और प्रॉपर्टी का इस्तेमाल इस तरह कर सकते हैं जैसे कि वे User क्लास का हिस्सा हों.
फ़ॉर्मैट किया गया नाम, User क्लास की प्रॉपर्टी है, न कि Repository क्लास का फ़ंक्शन. इसलिए, एक्सटेंशन प्रॉपर्टी का इस्तेमाल करते हैं. हमारी Repository फ़ाइल अब ऐसी दिखती है:
val User.formattedName: String
get() {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
object Repository {
private val _users = mutableListOf<User>()
val users: List<User>
get() = _users
val formattedUserNames: List<String>
get() {
return _users.map { user -> user.formattedName }
}
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
_users.add(user1)
_users.add(user2)
_users.add(user3)
}
}
Kotlin Standard Library, कई Java API की सुविधाओं को बढ़ाने के लिए एक्सटेंशन फ़ंक्शन का इस्तेमाल करती है. Iterable और Collection पर मौजूद कई सुविधाएं, एक्सटेंशन फ़ंक्शन के तौर पर लागू की जाती हैं. उदाहरण के लिए, पिछले चरण में इस्तेमाल किया गया map फ़ंक्शन, Iterable पर एक एक्सटेंशन फ़ंक्शन है.
11. स्कोप फ़ंक्शन: let, apply, with, run, also
हमारे Repository क्लास कोड में, हम _users सूची में कई User ऑब्जेक्ट जोड़ रहे हैं. Kotlin के स्कोप फ़ंक्शन की मदद से, इन कॉल को ज़्यादा मुहावरेदार बनाया जा सकता है.
किसी ऑब्जेक्ट के नाम के आधार पर उसे ऐक्सेस किए बिना, सिर्फ़ उसके कॉन्टेक्स्ट में कोड को लागू करने के लिए, Kotlin पांच स्कोप फ़ंक्शन उपलब्ध कराता है: let, apply, with, run, और also. इन फ़ंक्शन की मदद से, कोड को आसानी से पढ़ा जा सकता है और उसे छोटा बनाया जा सकता है. स्कोप फ़ंक्शन में एक रिसीवर (this) होता है. इसमें एक आर्ग्युमेंट (it) हो सकता है और यह वैल्यू भी दिखा सकता है.
यहां एक काम की चीट शीट दी गई है. इससे आपको यह याद रखने में मदद मिलेगी कि किस फ़ंक्शन का इस्तेमाल कब करना है:

हम Repository में अपने _users ऑब्जेक्ट को कॉन्फ़िगर कर रहे हैं. इसलिए, apply फ़ंक्शन का इस्तेमाल करके, कोड को ज़्यादा बेहतर बनाया जा सकता है:
init {
val user1 = User("Jane", "")
val user2 = User("John", null)
val user3 = User("Anne", "Doe")
_users.apply {
// this == _users
add(user1)
add(user2)
add(user3)
}
}
12. आखिर में खास जानकारी
इस कोडलैब में, हमने बुनियादी बातों के बारे में बताया है. इनकी मदद से, अपने कोड को Java से Kotlin में बदला जा सकता है. यह कन्वर्ज़न, आपके डेवलपमेंट प्लैटफ़ॉर्म से अलग होता है. इससे यह पक्का करने में मदद मिलती है कि आपने जो कोड लिखा है वह Kotlin के हिसाब से सही है.
Idiomatic Kotlin की मदद से, कोड को छोटा और आसान बनाया जा सकता है. Kotlin की सभी सुविधाओं की मदद से, अपने कोड को ज़्यादा सुरक्षित, छोटा, और पढ़ने में आसान बनाया जा सकता है. उदाहरण के लिए, हम init ब्लॉक को हटाकर, सीधे तौर पर एलान में उपयोगकर्ताओं के साथ Repository सूची को इंस्टैंशिएट करके, अपनी Repository क्लास को भी ऑप्टिमाइज़ कर सकते हैं:_users
private val users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
हमने कई विषयों को कवर किया है. जैसे, नल वैल्यू को हैंडल करना, सिंगलटन, स्ट्रिंग, और कलेक्शन. साथ ही, एक्सटेंशन फ़ंक्शन, टॉप-लेवल फ़ंक्शन, प्रॉपर्टी, और स्कोप फ़ंक्शन जैसे विषय. हमने दो Java क्लास को दो Kotlin क्लास में बदल दिया है. अब ये इस तरह दिखती हैं:
User.kt
data class User(var firstName: String?, var lastName: String?)
Repository.kt
val User.formattedName: String
get() {
return if (lastName != null) {
if (firstName != null) {
"$firstName $lastName"
} else {
lastName ?: "Unknown"
}
} else {
firstName ?: "Unknown"
}
}
object Repository {
private val _users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
val users: List<User>
get() = _users
val formattedUserNames: List<String>
get() = _users.map { user -> user.formattedName }
}
यहां Java के फ़ंक्शन और Kotlin में उनके मैपिंग की खास जानकारी दी गई है:
Java | Kotlin |
|
|
|
|
|
|
ऐसी क्लास जो सिर्फ़ डेटा सेव करती है |
|
कंस्ट्रक्टर में वैरिएबल को वैल्यू असाइन करना |
|
|
|
सिंगलटन क्लास |
|
Kotlin के बारे में ज़्यादा जानने और इसे अपने प्लैटफ़ॉर्म पर इस्तेमाल करने का तरीका जानने के लिए, ये संसाधन देखें:
- Kotlin Koans
- Kotlin के ट्यूटोरियल
- Android Kotlin Fundamentals
- प्रोग्रामर के लिए Kotlin बूटकैंप
- Java डेवलपर के लिए Kotlin - ऑडिट मोड में उपलब्ध मुफ़्त कोर्स