چرا کاتلین؟ ۸ دلیل که میتواند برنامهنویسان جاوا را برای تغییر قانع کند. (قسمت سوم)
جاوا به چه شکلی در میآمد اگر کسی آن را امروز از نو طراحی میکرد؟ احتمالا چیزی شبیه کاتلین
در قسمت اول این مطلب، در مورد سینتکس و تایپسیستم زبان کاتلین خواندیم. در صورتی که هنوز قسمت اول را نخواندهاید پیشنهاد میشود ابتدا آن را مطالعه کنید.
در قسمت دوم نیز، ایمنی در برابر null، توابع و برنامهنویسی تابعی، کلاسهای داده و توابع و فیلدهای افزودنی را از امکانات کاتلین مورد بررسی قرار دادیم.
در این قسمت، به امکان سربار کردن اپراتورها و اشیای سطح بالا و الگوی سینگلتون در کاتلین میپردازیم و در آخر نیز یک جمعبندی خواهیم داشت.
۷- سربار کردن اپراتورها
در کاتلین برخی توابع میتوانند از طریق اپراتورهای از پیش تعریفشده فراخوانی شوند. من چون در زندگی قبلیام یک ریاضیدان بودهام(!) به این قابلیت ارادت خاصی دارم. با کمک سربار کردن اپراتورها، بسیاری از عملیاتها روی کلاسهای ریاضیاتی میتوانند به شکل طبیعیتری نوشته شوند. مثلا کلاسهای Fraction و Matrix از اپراتورهای سربارشدهی جمع و ضرب و … استفاده میکنند. حتی کلاسهای غیر ریاضی هم میتوانند از مزیت سربار کردن اپراتورها استفاده کنند. مثلا استفاده از اپراتور == به جای تابع equals یا استفاده از براکت ([ ]) به جای تابع get (مثلا داخل myList[n] یا myMap[“key”]) میتواند مفید باشد.
البته زبان ++C هم امکان سربار کردن اپراتورها را میدهد اما بهشخصه رویکرد کاتلین را ترجیح میدهم.
در ادامه لیست (ناکاملی) از توابعی که با استفاده از اپراتورها میتوانند فراخوانی شوند را میبینیم:
نام تابع | عملگر |
()plus | + |
()times | * |
()plusAssign | =+ |
()equals | == |
()compareTo | > |
()get | [] |
()rangeTo | .. |
()contains | in |
()inc | ++ |
قوانین سربار کردن اپراتور در کاتلین
در مورد سربار کردن اپراتور در کاتلین خوب است به چند نکته مهم توجه کنیم:
- توابعی که قرار است اپراتوری را سربار کنند، باید با کلیدواژه operator مشخص شوند. این مورد در تکه کد زیر بهتر نشان داده میشود.
- وقتی این قابلیت با تایپسیستم یکپارچه کاتلین ترکیب میشود، به ما امکان استفاده از == را بدون نیاز به نگرانی بابت اینکه چه زمانی باید از این تابع و چه زمانی از equals استفاده کنیم میدهد. همه ماها در جایی از برنامهنویسی جاوا به جای equals به اشتباه از == استفاده کردهایم. همچنین برای چک کردن هویت (برابری رفرنسها) کاتلین عملگر === (۳ تا مساوی) را تعبیه کرده است. دقت کنید که حتی در صورتی که در بررسی برابری a==b اگر a برابر null باشد باز هم جای نگرانی نیست چرا که آن وقت به برابری === تبدیل میشود یعنی null === b.
- با اینکه برای ++ فقط یک تابع داریم (inc) اما کاتلین همواره به درستی از نتیجه این تابع استفاده میکند و x++ و ++x به شکلهای متفاوتی کامپایل میشوند. به شکل مشابه کاتلین میداند که زمانی که از اپراتورهای >= و < و .. استفاده میشود از نتیجه تابع compareTo چگونه استفاده کند.
در کد زیر یک کلاس را میبینید که چند اپراتور را سربار کرده است.
class Fraction(numerator : Long, denominator : Long = 1) : Comparable { val numerator : Long val denominator : Long init { ... // code to "normalize a fraction; e.g., 2/4 is normalized to 1/2 } fun toDouble() = numerator.toDouble()/denominator.toDouble() operator fun plus(f: Fraction): Fraction { val numer = numerator*f.denominator + denominator*f.numerator val denom = denominator*f.denominator return Fraction(numer, denom) } operator fun minus(f: Fraction): Fraction { val numer = numerator*f.denominator - denominator*f.numerator val denom = denominator*f.denominator return Fraction(numer, denom) } operator fun times(f: Fraction) = Fraction(numerator*f.numerator, denominator*f.denominator) operator fun div(f: Fraction): Fraction { if (f.numerator == 0L) throw IllegalArgumentException("Divide by zero.") return Fraction(numerator*f.denominator, denominator*f.numerator) } operator fun inc() = Fraction(numerator + denominator, denominator) operator fun unaryMinus() = Fraction(-numerator, denominator) override fun toString() = "$numerator/$denominator" ... // other functions such as compareTo(), hashCode(), and equals() }
و در ادامه یک نمونه استفاده از آن کلاس را میتوانید ببینید. دقت کنید که با کمک { } از قالبهای رشته (string template) استفاده شده است.
val f1 = Fraction(4, 10) // normalized to 2/5 val f2 = Fraction(5) val f3 = Fraction(2, 5) println("f1 = $f1 f2 = $f2") // f1 = 2/5 f2 = 5/1 println("f1.toDouble() = ${f1.toDouble()}") // f1.toDouble() = 0.4 println("f2.toDouble() = ${f2.toDouble()}") // f2.toDouble() = 5.0 println("f1 === f3 is ${f1 === f3}") // f1 === f3 is false println("f1 == f3 is ${f1 == f3}") // f1 == f3 is true println("f1 < f2 is ${f1 < f2}") // f1 < f2 is true println("f1.compareTo(f2) is ${f1.compareTo(f2)}") // f1.compareTo(f2) is -1 println("-f1 = ${-f1}") // -f1 = -2/5 println("f1 + f2 = ${f1 + f2}") // f1 + f2 = 27/5 println("f1 - f2 = ${f1 - f2}") // f1 - f2 = -23/5 println("f1 * f2 = ${f1*f2}") // f1 * f2 = 2/1 println("f1 / f2 = ${f1/f2}") // f1 / f2 = 2/25 var x : Fraction val y = Fraction(1,2) var z = Fraction(1) x = y + z++ println("x = $x, z = $z") // x = 3/2, z = 2/1 z = Fraction(1) x = y + ++z println("x = $x, z = $z") // x = 5/2, z = 2/1
اشیا سطح بالا و الگوی سینگلتون
برخی کلاسها باید تنها و تنها یک نمونه یا instance داشته باشند. معمولا این کلاسها وظیفه مدیریت متمرکز یک منبع را بر عهده دارند. مثلا پرینتر، لاگر و یا کلاس «کارخانه» برای ساخت اشیای دیگر و یک آبجکت برای مدیریت اتصالها به پایگاه داده. به کلاسهایی که دقیقا یک شی قابل دسترس از کل برنامه دارند، سینگلتون میگویند. (در مورد الگوی طراحی سینگلتون بیشتر بخوانید)
راههای متفاوتی برای پیادهسازی این الگوی طراحی در جاوا وجود دارد. یگ روش مرسوم (در صورتی که در محیط همروند نباشید) تعریف یک فیلد استاتیک و تابع استاتیک getInstance است. با این روش (مثل کد زیر) میتوان مقداردهی اولیه این فیلد را به شکل lazy هم در آورد، در این صورت تا زمانی که واقعا از شی استفاده نشود، شی مربوطه ساخته نمیشود. در این روش برای دستیابی به امنیت در محیط همروند، میتوان متد getInstance را سنکرون کرد.
public class Singleton { // the one and only instance private static Singleton instance = null; ... // other fields protected Singleton() { ... // initialization code } public static Singleton getInstance() { if (instance == null) instance = new Singleton(); return instance; } ... // other methods }
استفاده از این کلاس به شکل زیر است:
Singleton.getInstance().someMethod();
یادداشت مترجم: دقت کنید که به لطف بارگذاری «تنبل» کلاسها در جاوا، همین که فیلد را به شکل static تعریف کنید و به شکل برخط مقدار دهید، کافی است تا از مزیت بارگذاری تنبل این شی بهرهمند شوید و نیاز به کار بیشتر نیست. همچنین دقت کنید که اگر لازم است کلاستان در محیط همروند امن باشد باید خودتان متدهایش را امن کنید و سنکرون کردن getIsntance کافی نیست.
در کاتلین، الگوی سینگلتون به کمک «تعریف شی سطح بالا» ممکن میشود (top-level object declaration). به استفاده از کلمه کلیدی object به جای class دقت کنید.
object Singleton { ... // properties and methods }
استفاده از متدهای این کلاس به شکل زیر خواهد بود:
Singleton.someMethod()
اگرچه به این شکل، روش کاتلین ساده و سرراست است، اما در صورتی که سازنده ما چند پارامتر داشته باشد با پیچیدگی مواجه میشود.
ویژگیهای بیشتر
اگرچه در این مطلب ۳ قسمتی چندین ویژگی مهم کاتلین را بررسی کردیم اما این زبان ویژگیهای بیشتری دارد که معرفی هر یک نیاز به مطلب جداگانهای دارد. همچنین این زبان نسبت به جاوا برخی ویژگیها را هم ندارد که جای خالیشان حس میشود. مثلا فیلدهای استاتیک.
آیا مهاجرت از جاوا به کاتلین ارزشش را دارد؟
بسیاری از زبانها برای بهبود زبانهای برنامهنویسی قبلی ساخته شدهاند. کاتلین و اسکالا و کلوژر برای جایگزینی جاوا آمدهاند. آیا تبلیغ گوگل برای جایگزینی جاوا با کاتلین در اندروید کافی است؟
برای بیش از ۲۰ سال، زبان ترجیحی من جاوا بوده است، اگرچه جاوا زبان بسیار خوبی است اما بدخواهان خود را هم دارد! در طول زمان جاوا تلاش کرده و تکامل یافته تا کاستیهایش برطرف شوند و کارایی برنامهنویسان افزایش پیدا کند. اما تکامل در یک زبان برنامهنویسی فعلی، نیاز به دقت قابل توجه در سازگاری با نسخههای قدیمی دارد، برای همین گاهی ساختن یک زبان جدید جایگزین بهتری است.
آیا کاتلین بهتر از جاوا است؟ در بسیاری از زمینهها جواب مثبت است. اما آیا این خوبیها ارزش مهاجرت را دارند؟ این سوالیست که خودتان باید جواب دهید اما قطعا کاتلین لیاقت توجه جدی شما را دارد.
همچنین دوستداران میتوانند از این مطالب نکات بیشتری بیاموزند:
https://proandroiddev.com/the-kotlin-guide-for-the-busy-java-developer-93dde84a77b7
https://medium.com/pinterest-engineering/kotlin-for-grumpy-java-developers-8e90875cb6ab
https://www.ideamotive.co/blog/a-complete-kotlin-guide-for-java-developers
منبع: https://www.infoworld.com/article/3396141/why-kotlin-eight-features-that-could-convince-java-developers-to-switch.html
با اندکی تغییر و تلخیص
.
.
.
.
با ما همراه باشید
آدرس کانال تلگرام: JavaCupIR@
آدرس اکانت توییتر: JavaCupIR@
آدرس صفحه اینستاگرام: javacup.ir
صفحه ویرگول: javcup
آدرس گروه لینکدین: Iranian Java Developers