بازتاب در کاتلین (reflection in kotlin)

بازتاب(reflection) مانند سایر زبان های برنامه نویسی، این ویژگی قدرتمند برای ما این امکان رو ایجاد میکنه که ساختار کلاس ها، توابع، ویژگی ها(properties) و سایر عناصر کدمون رو در زمان اجرا بررسی کنیم. در حالی که استفاده از این ویژگی میتونه تاثیر مثبت یا منفی روی عملکرد کلی پروژه داشته باشه باید به دقت ازش استفاده کرد و موارد زیادی هست که میشه از reflection به صورت مفید استفاده کرد .در این پست، با اصول reflection درکاتلین آشنا میشیم و چند نمونه ساده رو بررسی می کنیم.درک reflection در کاتلینقابلیت reflection در Kotlin امکان بازرسی و ارتباط با کد در زمان اجرا رو به ما میده. یعنی ما می‌تونیم ساختار اشیا، کلاس‌ها و توابع را تجزیه و تحلیل کنیم، توابع را فراخوانی کنیم و به خصوصیات به صورت پویا دسترسی داشته باشیم بدون اینکه در زمان کامپایل از اون‌ها اطلاع داشته باشیم. با اپراتور :: امکان استفاده از این ویژگی رو داریم.مرجع کلاس (class reference)ابتدایی ترین حالت ایجاد یه reference از کلاسه، داخل کد زیر یه نمونه ساده از کلاس کاتلین رو داریم :class Dog(var name: String) { fun bark() { println(&quotBark!&quot) } fun bark(sound: String) { println(sound) } private fun hello() { println(&quotHello! My name is $name&quot) } }حالا میتونیم ازش یه refrence ایجاد کنیم :val classRef = Dog::classهمچنین، این امکان وجود داره که refrence کلاس رو از یک نمونه که قبلا ایجاد کردیم ، بگیریم:(با همون دستور class::)val myDog = Dog("Puppy")val classRef = myDog::classهنگامی که مرجع کلاس رو ایجاد کردیم، میتونیم به ویژگی های refrence دسترسی داشته باشید تا در مورد اون کلاس اطلاعات بیشتری دریافت کنیم. به عنوان مثال، میشه نام کلاس رو پیدا کرد یا بررسی کرد که آیا کلاس ما یه دیتاکلاس هست یا نه :println(classRef.simpleName) // Dog println(classRef.qualifiedName) // org.metapx.Dog println(classRef.isData) // falseدر Kotlin، مرجع کلاس به عنوان نوع Kclass شناخته میشه که مخفف Kotlin class هستش.جدا از بررسی کلاس، Kclass توانایی های جالبی داره به عنوان مثال، متد ()createInstance به ما اجازه می ده یه شی جدید از مرجع کلاس ایجاد کنید:val secondDog = classRef.createInstance()دسترسی به متدهای مرجع کلاس کاتلینما همچنین می‌تونیم به متدهای مرجع کلاس بدون در نظر گرفتن سطح دسترسی بهشون دسترسی داشته باشیم (private, protected, internal,public) . یعنی حتی میشه به توابعprivateیه کلاس نیز از مرجع اون دسترسی داشت:val myDog = Dog(&quotPuppy&quot) val classRef = myDog::class classRef.memberFunctions.forEach { println(it.name) }نکته : ویژگی MemberFunctions مربوط به Kclass میشه که تمام متدهای کلاس رو به عنوان یک مجموعه ذخیره می کنه.خروجی کد بالا به شکل زیر چاپ میشه:bark bark hello equals hashCode toStringدر ادامه می توانیم تابع رو از مرجع کلاس به صورت زیر صدا بزنیم:val myDog = Dog(&quotPuppy&quot) val classRef = myDog::classابتدا باید از تابع find برای پیدا کردن تابع مورد نظرمون از کلاس مرجع استفاده کنیم :val barkRef = classRef.memberFunctions.find { it.name == &quotbark&quot } barkRef?.call(myDog)تابع bark اگه موجود باشه (که هست)رو داخل متغیر barkRef ذخیره میکنیم اگه هم متدی رو سرچ کنیم و نباشه براش null ست میشه و بررسی میکنیم اگر null نبود با استفاده از متد call() از نوع مرجع تابع Kfunction استفاده میکنیم تا متدرو صدا بزنیم.اولین آرگومان متد call() باید نمونه ای از مرجع کلاس باشه، به همین دلیله که شی myDog به متد ارسال می شه. وقتی متد ما شما private باشه، باید قبل از فراخوانی متد، ویژگی isAccessible مرجع تابع را به عنوان true تغییر دهید:val helloRef = classRef.memberFunctions.find { it.name == &quothello&quot } helloRef?.isAccessible = true helloRef?.call(myDog)دسترسی به خصوصیات (properties) مرجع کلاس کاتلیندسترسی به property مرجع کلاس Kotlin مشابه روشیه که ما به متدهای اون دسترسی داریم . propertyهای یک کلاس در MemberProperties به عنوان یک مجموعه ذخیره می شن. به عنوان مثال، می تونیم مقدار property نام نمونه myDog را به صورت زیر دریافت کنیم:val myDog = Dog(&quotPuppy&quot) val classRef = myDog::class val nameRef = classRef.memberProperties.find { it.name == &quotname&quot } println(nameRef?.getter?.call(myDog)) // Puppyمرجع property نمونه ای از نوع KProperty هستش که مقدار property با فراخوانی متد getter() بازیابی میشه. برای تغییر مقدار name، باید ابتدا این property را مانند شکل زیر به KMutableProperty کست (cast) کنیم:val myDog = Dog(&quotPuppy&quot) val classRef = myDog::class val nameRef = classRef.memberProperties.find { it.name == &quotname&quot } as KMutableProperty<*>? nameRef?.setter?.call(myDog, &quotJacob&quot) println(myDog.name) // Jacobکلاس KMutableProperty متد ()setter را نگه می‌داره که برای تنظیم مقدار property باید اون را صدا بزنیم. تا اینجا یاد گرفته‌ایم که چطور از یک مرجع کلاس به متدها و propertyها دسترسی داشته باشیم. نتیجه گیری امکان Reflection یک ویژگی قدرتمنده که فقط برای نیازهای خاص استفاده میشه. به دلیل توانایی های که ذکر شد، بیشتر برای توسعه یه framework یا library برای توسعه بیشتر استفاده میشه که میشه به framework های JUnit و Spring اشاره کرد که از reflection در کدهاشون استفاده کردن.Reflection به فریم ورک اجازه می‌ده تا با کلاس‌ها و توابع بدون اینکه از قبل در مورد اونها اطلاعاتی داشته باشه، برخورد کنه.سعی کردم reflection در کاتلین رو تا جای ممکن روان توضیح بدم ، انشاالله که نتیجه مثبتی داشته باشه .