इस कोडलैब में, आपको Kotlin कोड लिखने या उसे अडैप्ट करने का तरीका बताया जाएगा, ताकि उसे Java कोड से आसानी से कॉल किया जा सके.
आपको क्या सीखने को मिलेगा
@JvmField
,@JvmStatic
, और अन्य एनोटेशन का इस्तेमाल कैसे करें.- Java कोड से Kotlin लैंग्वेज की कुछ सुविधाओं को ऐक्सेस करने से जुड़ी सीमाएं.
आपको पहले से क्या पता होना चाहिए
यह कोडलैब, प्रोग्रामर के लिए लिखा गया है. इसमें यह मानकर चला गया है कि आपको Java और Kotlin की बुनियादी जानकारी है.
इस कोडलैब में, Java प्रोग्रामिंग लैंग्वेज में लिखे गए किसी बड़े प्रोजेक्ट के कुछ हिस्से को माइग्रेट करने का तरीका बताया गया है, ताकि उसमें नया Kotlin कोड शामिल किया जा सके.
आसान तरीके से समझने के लिए, हम एक .java
फ़ाइल बनाएंगे, जिसका नाम UseCase.java
होगा. यह मौजूदा कोडबेस को दिखाएगी.
हम मान लेते हैं कि हमने Java में लिखे गए कुछ फ़ंक्शन को Kotlin में लिखे गए नए वर्शन से बदल दिया है. अब हमें इसे इंटिग्रेट करना है.
प्रोजेक्ट इंपोर्ट करना
प्रोजेक्ट के कोड को GitHub प्रोजेक्ट से यहां क्लोन किया जा सकता है: GitHub
इसके अलावा, यहां दिए गए ज़िप संग्रह से प्रोजेक्ट को डाउनलोड और एक्सट्रैक्ट किया जा सकता है:
अगर IntelliJ IDEA का इस्तेमाल किया जा रहा है, तो "Import Project" चुनें.
अगर Android Studio का इस्तेमाल किया जा रहा है, तो "Import project (Gradle, Eclipse ADT, etc.)" को चुनें.
आइए, UseCase.java
खोलें और हमें दिखने वाली गड़बड़ियों को ठीक करना शुरू करें.
registerGuest
फ़ंक्शन में समस्या है:
public static User registerGuest(String name) {
User guest = new User(Repository.getNextGuestId(), StringUtils.nameToLogin(name), name);
Repository.addUser(guest);
return guest;
}
Repository.getNextGuestId()
और Repository.addUser(...)
, दोनों के लिए गड़बड़ियां एक जैसी हैं: "स्टैटिक कॉन्टेक्स्ट से नॉन-स्टैटिक को ऐक्सेस नहीं किया जा सकता."
अब Kotlin की किसी फ़ाइल पर नज़र डालते हैं. Repository.kt
फ़ाइल खोलें.
हम देखते हैं कि हमारा रिपॉज़िटरी एक सिंगलटन है, जिसे ऑब्जेक्ट कीवर्ड का इस्तेमाल करके घोषित किया गया है. समस्या यह है कि Kotlin, हमारी क्लास में एक स्टैटिक इंस्टेंस जनरेट कर रहा है. हालांकि, इसे स्टैटिक प्रॉपर्टी और तरीकों के तौर पर दिखाना चाहिए.
उदाहरण के लिए, Repository.getNextGuestId()
को Repository.INSTANCE.getNextGuestId()
का इस्तेमाल करके रेफ़र किया जा सकता है. हालांकि, ऐसा करने का एक बेहतर तरीका भी है.
हम Kotlin से स्टैटिक तरीके और प्रॉपर्टी जनरेट करा सकते हैं. इसके लिए, हमें Repository की सार्वजनिक प्रॉपर्टी और तरीकों को @JvmStatic
से एनोटेट करना होगा:
object Repository {
val BACKUP_PATH = "/backup/user.repo"
private val _users = mutableListOf<User>()
private var _nextGuestId = 1000
@JvmStatic
val users: List<User>
get() = _users
@JvmStatic
val nextGuestId
get() = _nextGuestId++
init {
_users.add(User(100, "josh", "Joshua Calvert", listOf("admin", "staff", "sys")))
_users.add(User(101, "dahybi", "Dahybi Yadev", listOf("staff", "nodes")))
_users.add(User(102, "sarha", "Sarha Mitcham", listOf("admin", "staff", "sys")))
_users.add(User(103, "warlow", groups = listOf("staff", "inactive")))
}
@JvmStatic
fun saveAs(path: String?):Boolean {
val backupPath = path ?: return false
val outputFile = File(backupPath)
if (!outputFile.canWrite()) {
throw FileNotFoundException("Could not write to file: $backupPath")
}
// Write data...
return true
}
@JvmStatic
fun addUser(user: User) {
// Ensure the user isn't already in the collection.
val existingUser = users.find { user.id == it.id }
existingUser?.let { _users.remove(it) }
// Add the user.
_users.add(user)
}
}
अपने आईडीई का इस्तेमाल करके, अपने कोड में @JvmStatic एनोटेशन जोड़ें.
अगर हम UseCase.java
पर वापस स्विच करते हैं, तो Repository
पर मौजूद प्रॉपर्टी और मेथड से अब गड़बड़ियां नहीं होंगी. हालांकि, Repository.BACKUP_PATH
से गड़बड़ियां हो सकती हैं. हम इसके बारे में बाद में बात करेंगे.
फ़िलहाल, registerGuest()
तरीके में मौजूद अगली गड़बड़ी को ठीक करते हैं.
यहां दिए गए उदाहरण पर विचार करें: हमारे पास स्ट्रिंग ऑपरेशन के लिए कई स्टैटिक फ़ंक्शन वाली StringUtils
क्लास थी. इसे Kotlin में बदलने के दौरान, हमने तरीकों को एक्सटेंशन फ़ंक्शन में बदल दिया. Java में एक्सटेंशन फ़ंक्शन नहीं होते. इसलिए, Kotlin इन तरीकों को स्टैटिक फ़ंक्शन के तौर पर कंपाइल करता है.
माफ़ करें, अगर हम UseCase.java
में registerGuest()
तरीके को देखें, तो हमें पता चलता है कि कुछ गड़बड़ी है:
User guest = new User(Repository.getNextGuestId(), StringUtils.nameToLogin(name), name);
इसकी वजह यह है कि Kotlin, इन "टॉप-लेवल" या पैकेज-लेवल फ़ंक्शन को ऐसी क्लास में रखता है जिसका नाम फ़ाइल के नाम पर आधारित होता है. इस मामले में, फ़ाइल का नाम StringUtils.kt है. इसलिए, इससे जुड़ी क्लास का नाम StringUtilsKt
है.
हम StringUtils
के सभी रेफ़रंस को StringUtilsKt
में बदलकर, इस गड़बड़ी को ठीक कर सकते हैं. हालांकि, यह तरीका सही नहीं है, क्योंकि:
- हमारे कोड में कई ऐसी जगहें हो सकती हैं जिन्हें अपडेट करने की ज़रूरत होगी.
- नाम ही अजीब है.
इसलिए, Java कोड को फिर से लिखने के बजाय, Kotlin कोड को अपडेट करते हैं, ताकि इन तरीकों के लिए किसी दूसरे नाम का इस्तेमाल किया जा सके.
StringUtils.Kt
खोलें और पैकेज का यह एलान ढूंढें:
package com.google.example.javafriendlykotlin
@file:JvmName
एनोटेशन का इस्तेमाल करके, Kotlin को पैकेज-लेवल के तरीकों के लिए किसी दूसरे नाम का इस्तेमाल करने के लिए कहा जा सकता है. आइए, इस एनोटेशन का इस्तेमाल करके क्लास StringUtils
का नाम तय करें.
@file:JvmName("StringUtils")
package com.google.example.javafriendlykotlin
अब अगर हम UseCase.java
पर वापस जाएं, तो हमें दिखेगा कि StringUtils.nameToLogin()
से जुड़ी गड़बड़ी ठीक हो गई है.
माफ़ करें, इस गड़बड़ी को ठीक करने के बाद, User
के कंस्ट्रक्टर में पास किए जा रहे पैरामीटर के बारे में एक नई गड़बड़ी हुई है. आइए, अगले चरण पर जाएं और UseCase.registerGuest()
में मौजूद इस आखिरी गड़बड़ी को ठीक करें.
Kotlin, पैरामीटर के लिए डिफ़ॉल्ट वैल्यू के साथ काम करती है. Repository.kt
के init
ब्लॉक में जाकर, यह देखा जा सकता है कि इनका इस्तेमाल कैसे किया जाता है.
Repository.kt:
_users.add(User(102, "sarha", "Sarha Mitcham", listOf("admin", "staff", "sys")))
_users.add(User(103, "warlow", groups = listOf("staff", "inactive")))
हम देख सकते हैं कि उपयोगकर्ता "warlow" के लिए, displayName
की वैल्यू को स्किप किया जा सकता है. ऐसा इसलिए, क्योंकि User.kt
में इसके लिए डिफ़ॉल्ट वैल्यू दी गई है.
User.kt:
data class User(
val id: Int,
val username: String,
val displayName: String = username.toTitleCase(),
val groups: List<String> = listOf("guest")
)
माफ़ करें, Java से इस तरीके को कॉल करने पर यह उसी तरह काम नहीं करता.
UseCase.java:
User guest = new User(Repository.getNextGuestId(), StringUtils.nameToLogin(name), name);
Java प्रोग्रामिंग लैंग्वेज में डिफ़ॉल्ट वैल्यू इस्तेमाल नहीं की जा सकतीं. इसे ठीक करने के लिए, Kotlin को @JvmOverloads एनोटेशन की मदद से, हमारे कंस्ट्रक्टर के लिए ओवरलोड जनरेट करने के लिए कहें.
सबसे पहले, हमें User.kt
में थोड़ा बदलाव करना होगा.
User
क्लास में सिर्फ़ एक प्राइमरी कंस्ट्रक्टर है और कंस्ट्रक्टर में कोई एनोटेशन शामिल नहीं है. इसलिए, constructor
कीवर्ड को हटा दिया गया है. अब हमें इस पर एनोटेशन जोड़ना है. हालांकि, इसमें constructor
कीवर्ड शामिल होना चाहिए:
data class User constructor(
val id: Int,
val username: String,
val displayName: String = username.toTitleCase(),
val groups: List<String> = listOf("guest")
)
constructor
कीवर्ड मौजूद होने पर, हम @JvmOverloads
एनोटेशन जोड़ सकते हैं:
data class User @JvmOverloads constructor(
val id: Int,
val username: String,
val displayName: String = username.toTitleCase(),
val groups: List<String> = listOf("guest")
)
UseCase.java
पर वापस जाने पर, हमें पता चलता है कि registerGuest
फ़ंक्शन में अब कोई गड़बड़ी नहीं है!
हमारा अगला चरण, user.hasSystemAccess()
में user.hasSystemAccess()
पर कॉल करने की सुविधा को ठीक करना है.UseCase.getSystemUsers()
इसके लिए, अगले चरण पर जाएं या @JvmOverloads
ने गड़बड़ी को ठीक करने के लिए क्या किया, इसके बारे में ज़्यादा जानने के लिए पढ़ना जारी रखें.
@JvmOverloads
@JvmOverloads
के काम करने के तरीके को बेहतर तरीके से समझने के लिए, आइए UseCase.java
में एक टेस्ट मेथड बनाएं:
private void testJvmOverloads() {
User syrinx = new User(1001, "syrinx");
User ione = new User(1002, "ione", "Ione Saldana");
List<String> groups = new ArrayList<>();
groups.add("staff");
User beaulieu = new User(1002, "beaulieu", groups);
}
हम सिर्फ़ दो पैरामीटर, id
और username
की मदद से User
बना सकते हैं:
User syrinx = new User(1001, "syrinx");
हम groups
के लिए डिफ़ॉल्ट वैल्यू का इस्तेमाल करते हुए, displayName
के लिए तीसरा पैरामीटर शामिल करके भी User
बना सकते हैं:
User ione = new User(1002, "ione", "Ione Saldana");
हालांकि, displayName
को स्किप करके, सिर्फ़ groups
के लिए वैल्यू नहीं दी जा सकती. इसके लिए, आपको अतिरिक्त कोड लिखना होगा:
इसलिए, आइए उस लाइन को मिटा दें या उसे ‘//' से पहले जोड़कर टिप्पणी के तौर पर मार्क कर दें.
Kotlin में, अगर हमें डिफ़ॉल्ट और नॉन-डिफ़ॉल्ट पैरामीटर को एक साथ इस्तेमाल करना है, तो हमें नाम वाले पैरामीटर का इस्तेमाल करना होगा.
// This doesn't work...
User(104, "warlow", listOf("staff", "inactive"))
// But using named parameters, it does...
User(104, "warlow", groups = listOf("staff", "inactive"))
इसकी वजह यह है कि Kotlin, कंस्ट्रक्टर के साथ-साथ फ़ंक्शन के लिए ओवरलोड जनरेट करेगा. हालांकि, यह डिफ़ॉल्ट वैल्यू वाले हर पैरामीटर के लिए सिर्फ़ एक ओवरलोड बनाएगा.
आइए, UseCase.java
पर वापस जाएं और अगली समस्या को हल करें: UseCase.getSystemUsers()
तरीके में user.hasSystemAccess()
को कॉल करना:
public static List<User> getSystemUsers() {
ArrayList<User> systemUsers = new ArrayList<>();
for (User user : Repository.getUsers()) {
if (user.hasSystemAccess()) { // Now has an error!
systemUsers.add(user);
}
}
return systemUsers;
}
यह एक दिलचस्प गड़बड़ी है! अगर क्लास User
पर अपने आईडीई की अपने-आप पूरा होने वाली सुविधा का इस्तेमाल किया जाता है, तो आपको दिखेगा कि hasSystemAccess()
का नाम बदलकर getHasSystemAccess()
कर दिया गया है.
इस समस्या को ठीक करने के लिए, हम चाहते हैं कि Kotlin, val
प्रॉपर्टी hasSystemAccess
के लिए कोई दूसरा नाम जनरेट करे. इसके लिए, हम @JvmName
एनोटेशन का इस्तेमाल कर सकते हैं. आइए, वापस User.kt
पर जाएं और देखें कि हमें इसे कहां लागू करना चाहिए.
हम एनोटेशन को दो तरीकों से लागू कर सकते हैं. पहला तरीका यह है कि इसे सीधे get()
तरीके पर लागू किया जाए. जैसे:
val hasSystemAccess
@JvmName("hasSystemAccess")
get() = "sys" in groups
इससे Kotlin को यह सिग्नल मिलता है कि वह साफ़ तौर पर तय किए गए getter के सिग्नेचर को दिए गए नाम में बदल दे.
इसके अलावा, get:
प्रीफ़िक्स का इस्तेमाल करके, इसे प्रॉपर्टी पर इस तरह लागू किया जा सकता है:
@get:JvmName("hasSystemAccess")
val hasSystemAccess
get() = "sys" in groups
वैकल्पिक तरीका, उन प्रॉपर्टी के लिए खास तौर पर मददगार होता है जो डिफ़ॉल्ट रूप से, इंप्लिसिट तौर पर तय किए गए गेटर का इस्तेमाल कर रही हैं. उदाहरण के लिए:
@get:JvmName("isActive")
val active: Boolean
इससे, getter को साफ़ तौर पर तय किए बिना, getter का नाम बदला जा सकता है.
इन दोनों में अंतर होने के बावजूद, अपनी पसंद के हिसाब से किसी भी विकल्प का इस्तेमाल किया जा सकता है. इन दोनों से, Kotlin hasSystemAccess()
नाम का एक गेटर बनाएगा.
UseCase.java
पर वापस जाने पर, हम पुष्टि कर सकते हैं कि getSystemUsers()
में अब कोई गड़बड़ी नहीं है!
अगली गड़बड़ी formatUser()
में है. हालांकि, अगर आपको Kotlin में गेटर का नाम रखने के तरीके के बारे में ज़्यादा जानना है, तो अगले चरण पर जाने से पहले यहां पढ़ें.
गैटर और सेटर के नाम रखने के बारे में जानकारी
Kotlin लिखते समय, यह भूलना आसान है कि इस तरह का कोड लिखना:
val myString = "Logged in as ${user.displayName}")
यह displayName
की वैल्यू पाने के लिए, किसी फ़ंक्शन को कॉल कर रहा है. इसकी पुष्टि करने के लिए, मेन्यू में Tools > Kotlin > Show Kotlin Bytecode पर जाएं. इसके बाद, Decompile बटन पर क्लिक करें:
String myString = "Logged in as " + user.getDisplayName();
जब हमें Java से इन्हें ऐक्सेस करना होता है, तो हमें गेटर का नाम साफ़ तौर पर लिखना होता है.
ज़्यादातर मामलों में, Kotlin प्रॉपर्टी के लिए गेटर का Java नाम, सिर्फ़ get
+ प्रॉपर्टी का नाम होता है. जैसा कि हमने User.getHasSystemAccess()
और User.getDisplayName()
के साथ देखा है. इसका एक अपवाद ऐसी प्रॉपर्टी हैं जिनके नाम "is" से शुरू होते हैं. इस मामले में, गेटर के लिए Java का नाम, Kotlin प्रॉपर्टी का नाम होता है.
उदाहरण के लिए, User
पर मौजूद कोई प्रॉपर्टी, जैसे कि:
val isAdmin get() = //...
इसे Java से ऐक्सेस किया जाएगा. इसके लिए:
boolean userIsAnAdmin = user.isAdmin();
@JvmName
एनोटेशन का इस्तेमाल करके, Kotlin ऐसा बाइटकोड जनरेट करता है जिसमें एनोटेट किए जा रहे आइटम के लिए, डिफ़ॉल्ट नाम के बजाय तय किया गया नाम होता है.
यह सेटर के लिए भी इसी तरह काम करता है. सेटर के जनरेट किए गए नाम हमेशा set
+ प्रॉपर्टी का नाम होते हैं. उदाहरण के लिए, यह क्लास लें:
class Color {
var red = 0f
var green = 0f
var blue = 0f
}
मान लें कि हमें सेटर का नाम setRed()
से बदलकर updateRed()
करना है. हालांकि, हमें गेटर के नाम में कोई बदलाव नहीं करना है. इसके लिए, हम @set:JvmName
वर्शन का इस्तेमाल कर सकते हैं:
class Color {
@set:JvmName("updateRed")
var red = 0f
@set:JvmName("updateGreen")
var green = 0f
@set:JvmName("updateBlue")
var blue = 0f
}
इसके बाद, Java से हम यह लिख पाएंगे:
color.updateRed(0.8f);
UseCase.formatUser()
, User
ऑब्जेक्ट की प्रॉपर्टी की वैल्यू पाने के लिए, फ़ील्ड को सीधे तौर पर ऐक्सेस करने की सुविधा का इस्तेमाल करता है.
Kotlin में, प्रॉपर्टी आम तौर पर गेटर और सेटर के ज़रिए दिखाई जाती हैं. इसमें val
प्रॉपर्टी शामिल हैं.
@JvmField
एनोटेशन का इस्तेमाल करके, इस व्यवहार को बदला जा सकता है. जब इसे किसी क्लास की प्रॉपर्टी पर लागू किया जाता है, तो Kotlin, getter (और var
प्रॉपर्टी के लिए setter) मेथड जनरेट नहीं करेगा. साथ ही, बैकिंग फ़ील्ड को सीधे तौर पर ऐक्सेस किया जा सकता है.
User
ऑब्जेक्ट में बदलाव नहीं किया जा सकता. इसलिए, हम उनकी हर प्रॉपर्टी को फ़ील्ड के तौर पर दिखाना चाहते हैं. इसलिए, हम उनमें से हर एक को @JvmField
के साथ एनोटेट करेंगे:
data class User @JvmOverloads constructor(
@JvmField val id: Int,
@JvmField val username: String,
@JvmField val displayName: String = username.toTitleCase(),
@JvmField val groups: List<String> = listOf("guest")
) {
@get:JvmName("hasSystemAccess")
val hasSystemAccess
get() = "sys" in groups
}
अब UseCase.formatUser()
को देखने पर पता चलता है कि गड़बड़ियां ठीक हो गई हैं!
@JvmField or const
इसके अलावा, UseCase.java
फ़ाइल में भी इसी तरह की एक और गड़बड़ी दिख रही है:
Repository.saveAs(Repository.BACKUP_PATH);
यहां ऑटोमैटिक भरने की सुविधा का इस्तेमाल करने पर, हमें पता चलता है कि Repository.getBACKUP_PATH()
मौजूद है. इसलिए, BACKUP_PATH
पर मौजूद एनोटेशन को @JvmStatic
से बदलकर @JvmField
करने का विकल्प मिल सकता है.
आइए, इसे आज़माएं. Repository.kt
पर वापस जाएं और एनोटेशन अपडेट करें:
object Repository {
@JvmField
val BACKUP_PATH = "/backup/user.repo"
अब UseCase.java
पर जाकर देखते हैं. हमें दिखेगा कि गड़बड़ी ठीक हो गई है. हालांकि, BACKUP_PATH
पर एक नोट भी दिखेगा:
Kotlin में, सिर्फ़ प्रिमिटिव टाइप const
हो सकते हैं. जैसे, int
, float
, और String
. इस मामले में, BACKUP_PATH
एक स्ट्रिंग है. इसलिए, @JvmField
के साथ एनोटेट किए गए val
के बजाय const val
का इस्तेमाल करके, बेहतर परफ़ॉर्मेंस पाई जा सकती है. साथ ही, वैल्यू को फ़ील्ड के तौर पर ऐक्सेस करने की सुविधा भी बनी रहती है.
आइए, अब इसे Repository.kt में बदलते हैं:
object Repository {
const val BACKUP_PATH = "/backup/user.repo"
UseCase.java
को देखने पर पता चलता है कि अब सिर्फ़ एक गड़बड़ी बची है.
आखिरी गड़बड़ी Exception: 'java.io.IOException' is never thrown in the corresponding try block.
है
अगर हम Repository.kt
में Repository.kt
के कोड को देखें, तो हमें पता चलता है कि यह एक अपवाद दिखाता है.Repository.saveAs
आपको कम्यूनिटी दिशा-निर्देशों और नीतियों का उल्लंघन करने वाला किस तरह का कॉन्टेंट मिला?
Java में "चेक्ड एक्सेप्शन" का कॉन्सेप्ट होता है. ये ऐसी समस्याएं हैं जिन्हें ठीक किया जा सकता है. जैसे, उपयोगकर्ता ने फ़ाइल का नाम गलत टाइप कर दिया हो या नेटवर्क कुछ समय के लिए उपलब्ध न हो. चेक किए गए अपवाद का पता चलने के बाद, डेवलपर उपयोगकर्ता को यह बता सकता है कि समस्या को कैसे ठीक किया जाए.
जांचे गए अपवादों की जांच कंपाइल टाइम पर की जाती है. इसलिए, इन्हें तरीके के सिग्नेचर में घोषित किया जाता है:
public void openFile(File file) throws FileNotFoundException {
// ...
}
दूसरी ओर, Kotlin में चेक किए गए अपवाद नहीं होते हैं. यही वजह है कि यहां समस्या आ रही है.
इसका समाधान यह है कि Kotlin से IOException
के सिग्नेचर में Repository.saveAs()
जोड़ने के लिए कहा जाए, ताकि JVM बाइटकोड इसे चेक किए गए अपवाद के तौर पर शामिल कर सके.
हम ऐसा Kotlin @Throws
एनोटेशन की मदद से करते हैं. इससे Java/Kotlin इंटरोऑपरेबिलिटी में मदद मिलती है. Kotlin में, अपवाद Java की तरह ही काम करते हैं. हालांकि, Java के उलट, Kotlin में सिर्फ़ अनचेक्ड अपवाद होते हैं. इसलिए, अगर आपको अपने Java कोड को यह बताना है कि Kotlin फ़ंक्शन एक अपवाद दिखाता है, तो आपको Kotlin फ़ंक्शन के सिग्नेचर में @Throws एनोटेशन का इस्तेमाल करना होगा. Repository.kt file
पर स्विच करें और saveAs()
को अपडेट करके, नया एनोटेशन शामिल करें:
@JvmStatic
@Throws(IOException::class)
fun saveAs(path: String?) {
val outputFile = File(path)
if (!outputFile.canWrite()) {
throw FileNotFoundException("Could not write to file: $path")
}
// Write data...
}
@Throws
एनोटेशन की मदद से, हम देख सकते हैं कि UseCase.java
में कंपाइलर से जुड़ी सभी गड़बड़ियां ठीक कर दी गई हैं! बहुत बढ़िया!
आपके मन में यह सवाल आ सकता है कि अब Kotlin से saveAs()
को कॉल करते समय, क्या आपको try
और catch
ब्लॉक का इस्तेमाल करना होगा.
नहीं! ध्यान रखें कि Kotlin में चेक किए गए अपवाद नहीं होते हैं. साथ ही, किसी तरीके में @Throws
जोड़ने से भी यह नहीं बदलता:
fun saveFromKotlin(path: String) {
Repository.saveAs(path)
}
जब अपवादों को हैंडल किया जा सकता है, तब उन्हें पकड़ना अब भी फ़ायदेमंद होता है. हालांकि, Kotlin आपको उन्हें हैंडल करने के लिए मजबूर नहीं करता.
इस कोडलैब में, हमने Kotlin कोड लिखने के बारे में बुनियादी जानकारी दी है. साथ ही, इसमें आम तौर पर इस्तेमाल होने वाले Java कोड को लिखने के बारे में भी बताया गया है.
हमने इस बारे में बात की कि एनोटेशन का इस्तेमाल करके, Kotlin के JVM बाइटकोड जनरेट करने के तरीके को कैसे बदला जा सकता है. जैसे:
@JvmStatic
का इस्तेमाल, स्टैटिक सदस्यों और तरीकों को जनरेट करने के लिए किया जाता है.@JvmOverloads
का इस्तेमाल करके, डिफ़ॉल्ट वैल्यू वाले फ़ंक्शन के लिए ओवरलोड किए गए तरीके जनरेट किए जा सकते हैं.@JvmName
का इस्तेमाल करके, गेटर और सेटर के नाम बदले जा सकते हैं.@JvmField
का इस्तेमाल करके, प्रॉपर्टी को सीधे तौर पर फ़ील्ड के तौर पर दिखाया जा सकता है. इसके लिए, गेटर और सेटर का इस्तेमाल करने की ज़रूरत नहीं होती.@Throws
का इस्तेमाल करके, चेक किए गए अपवादों का एलान किया जाता है.
हमारी फ़ाइलों का फ़ाइनल कॉन्टेंट यह है:
User.kt
data class User @JvmOverloads constructor(
@JvmField val id: Int,
@JvmField val username: String,
@JvmField val displayName: String = username.toTitleCase(),
@JvmField val groups: List<String> = listOf("guest")
) {
val hasSystemAccess
@JvmName("hasSystemAccess")
get() = "sys" in groups
}
Repository.kt
object Repository {
const val BACKUP_PATH = "/backup/user.repo"
private val _users = mutableListOf<User>()
private var _nextGuestId = 1000
@JvmStatic
val users: List<User>
get() = _users
@JvmStatic
val nextGuestId
get() = _nextGuestId++
init {
_users.add(User(100, "josh", "Joshua Calvert", listOf("admin", "staff", "sys")))
_users.add(User(101, "dahybi", "Dahybi Yadev", listOf("staff", "nodes")))
_users.add(User(102, "sarha", "Sarha Mitcham", listOf("admin", "staff", "sys")))
_users.add(User(103, "warlow", groups = listOf("staff", "inactive")))
}
@JvmStatic
@Throws(IOException::class)
fun saveAs(path: String?):Boolean {
val backupPath = path ?: return false
val outputFile = File(backupPath)
if (!outputFile.canWrite()) {
throw FileNotFoundException("Could not write to file: $backupPath")
}
// Write data...
return true
}
@JvmStatic
fun addUser(user: User) {
// Ensure the user isn't already in the collection.
val existingUser = users.find { user.id == it.id }
existingUser?.let { _users.remove(it) }
// Add the user.
_users.add(user)
}
}
StringUtils.kt
@file:JvmName("StringUtils")
package com.google.example.javafriendlykotlin
fun String.toTitleCase(): String {
if (isNullOrBlank()) {
return this
}
return split(" ").map { word ->
word.foldIndexed("") { index, working, char ->
val nextChar = if (index == 0) char.toUpperCase() else char.toLowerCase()
"$working$nextChar"
}
}.reduceIndexed { index, working, word ->
if (index > 0) "$working $word" else word
}
}
fun String.nameToLogin(): String {
if (isNullOrBlank()) {
return this
}
var working = ""
toCharArray().forEach { char ->
if (char.isLetterOrDigit()) {
working += char.toLowerCase()
} else if (char.isWhitespace() and !working.endsWith(".")) {
working += "."
}
}
return working
}