اپراتورهای == و === و .equals در کاتلین و جاوا

کدام یک از گزینه های زیر صحیح است؟ (https://t.me/imandroid/51)

اپراتور == در جاوا و کاتلین یه یک معنی می باشند
اپراتور == در جاوا محتوا و مقدار دو متغیر یا شی را مقایسه می کند
اپراتور === در کاتلین معادل اپراتور == در جاوا است<br/>تابع .equals() در جاوا معادل اپراتور === در کاتلین است

✅(سطح سوال متوسط) - جواب صحیح گزینه ی ۳ است.

❇️عملگر === در کاتلین و عملگر == در جاوا هر دو رفرنسهای دو طرف را مقایسه میکنند.

نصف کنیم؟
نصف کنیم؟

🔶 عملگر == در کاتلین محتوای دو متغیر را با هم مقایسه میکند در حالی که در جاوا اصلا اینطور نیست و این عملگر رفرنس دو متغیر را مقایسه می کند.

بگذارید اول تکلیف مون را با جاوا مشخص کنیم بعد بریم سراغ کاتلین(اگه جاوا اوکی اید برید سراغ بخش کاتلین) :

🔶 فرض کنید در جاوا یک شی به نام person داریم که توی سازنده ش هم اسم رو میگیره :

Person person1 = new Person(“Iman”)

Person person2 = new Person(“Iman”)

— —

(person1 == person2) —> false

همانطور که میدونید اینجا person1 و person2 صرفا یک رفرنس هستند به آدرس اصلی شی ساخته شده در حافظه ی Heap که مسلما با هم برابر نیستند.(در واقع آدرس ها یک عدد هستند)

🔶 اما اگر به جای شی ، دو متغیر پایه ای (primitive variable) داشتیم:

int a = 10;
int b = 10;
— —
(a == b) —> true

نتیجه true است چرا که در primitive variable ها دقیقا خود مقدار درون متغیر ذخیره می شود ( در حافظه ی stack)

🔶 اما تابع .equals در جاوا محتوای اون دو خانه ی حافظه رو بر میداره و با هم مقایسه میکنه:

(person1.equals(person2) ) —> false

باز هم جواب false است. اینجا در محتوای هر دو خانه شی هایی از مدل Person ساخته شده که اسم هر دوی آنها هم Iman است! اما خب اینها دو شی جدا هستند و قطعا یکی نیستند! ( خب این همه Iman داریم همه شون یکی ان؟😆)

🔶 اما خب راجع به String ها کمی داستان متفاوت است.
اول از همه به این تعریف نگاه کنید:

String name1 = “iman”;

حالا به این هم نگاه کنید:

String name1 = new string("Iman");

🔶 در واقع String یک کلاس است و یک متغیر primitive نیست پس قاعدتا نحوه ی صحیح تعریف یک متغیر از string حالت دوم است اما نکته ای که وجود داره اینه که جاوا برای string یک استثنا قائل شده و اجازه داده که به صورت اول هم بتونیم از روش شی بسازیم.
🔶 که خب البته تفاوت فقط توی همین نوشتن نیست و توی نحوه ی ذخیره سازی هم تفاوت دارند. جاوا توی حالت اول میاد و مقدار رو توی یک string pool ذخیره میکنه توی این حالت اگه شما به مجددا یک مقدار مشابه رو بخواید توی یک متغیر استرینگ بریزید دیگه یه خونه ی جدید از حافظه رو بهش اختصاص نمیده و به همون خونه ی قبلی از حافظه اشاره میکنه. فقط کافیه به عکس زیر نگاه کنید تا دیگه همه چی روشن بشه.

این هم اضافه کنیم تمومه - s1.equals(s3) --> true
این هم اضافه کنیم تمومه - s1.equals(s3) --> true

🔵 اما در کاتلین اوضاع از چه قرار است؟

🔹 عملگر === در کاتلین دقیقا همان نقش عملگر == در جاوا رو ایفا میکند چرا که رفرنس های دو طرف را فقط مقایسه می کند ( که هر دو به یک خانه از حافظه اشاره میکنند یا نه یا به عبارتی آیا هردو دقیقا یک شی هستند یا نه) و خب نقیض این عملگر هم ( ==! ) است.

🔹اما همانطور که در ابتدای متن هم اشاره کردم عملگر == در کاتلین را رسما میتونیم بگیم مشابه .equals در جاوا است چرا که مقدار دو طرف را باهم مقایسه میکند.
اما نکته ی مهمی این وسط توی کاتلین راجع به data class ها وجود داره! خوب به این مثال دقت کنید :

class Person (val name: String)
val person1 = Person(“Iman”)

val person2 = Person(“Iman”)

— —

(person1 == person2) —> false
(person1 === person2) —> false
(person1.name == person2.name) —> true

🔹همونطور که می بینید دقیقا مثل جاوا در دو طرف تساوی دوی شی person که همنام هم هستند قرار دارند که چون دو شی متفاوت هستند با هم برابر نیستند. اما کاتلین در data class خیلی با شعور می شود و میاد و ویژگی های دو شی را با هم مقایسه میکند. دقت کنید:

data class Person (val name: String)

(person1 == person2) —> true
(person1 === person2) —> true
(person1.name == person2.name) —> true

🔹اما تابع equals در کاتلین به چه معنی است؟ این هم دقیقا مثل == در کاتلین عمل میکنه یعنی مقدار و محتوای دو طرف رو مقایسه میکنه. اما خب یک تفاوت ریز هم داره که اون هم توی اعداد اعشاری هستش که برای توضیحش به چندتا پیش زمینه نیاز داریم:
یک اینکه قرارداد IEEE754 چی هستش ، که راجع بهش توی پست های اول کانال تلگرام کلی صحبت کردم.( اینجا رو ببینید)
دوم اینکه در اعداد اعشاری Nan ها چی هستند دقیقا که این رو همینجا یه مروری میکنیم:
در واقع Nan مخفف Not a number است . همین ۳۴ سال پیش (سال ۱۹۸۵) برای اینکه cpu ها برای محاسبات شون از یک استاندارد پیروی کنند سازمان IEEE میاد و یک استاندارد رو به نام IEEE754 اعلام میکنه که الان تقریبا همه ی cpu ها از اون پیروی می کنند.
به طوری کلی توی این استاندارد راجع به نحوه ی محاسبه و نمایش اعداد اعشاری قرارداد هایی شده که یکی از موارد این قرارداد مشخص میکنه که یک سری از اعداد که غیر قابل نمایش یا بدون تعریف هستند(یا حتی زمانی که خطایی رخ میدهد) باید Nan نشان داده شوند.(مثلا تقسیم بر صفر)
توی جاوا هم Double.NaN و Float.NaN رو هم میتونید پیدا کنید.که در تعریفشون هم گفته شده:

🔹حالا نکته ی کار ما اینجاست که این Nan ها امکان مقایسه با هم رو هم دارند و این رفتاری که توی مقایسه کردنها از خودشون نشون میدن کلی بحث ایجاد کرده که آیا مطابق با قرارداد IEEE754 رفتار میکنند یا نه. که اینجا در کاتلین تابع equal برای Nan ها مخالف با قانون IEEE754 رفتار میکند . به طور مثال :

val negZero = -0.0f
val posZero = 0.0f

println(negZero == posZero) //true
println(negZero.equals(posZero)) //false
println(negZero === posZero) //true

برای توضیحات بیشتر به لینک های زیر میتونید سر بزنید:

https://medium.com/@agrawalsuneet/equality-in-kotlin-and-equals-d8373ef529f1

https://kotlinlang.org/docs/reference/equality.html#floating-point-numbers-equality