כיצד ליצור מחלקה בלתי ניתנת לשינוי בג'אווה?

ב-Java immutability פירושה שברגע שנוצר אובייקט לא ניתן לשנות את המצב הפנימי שלו. מחלקות בלתי ניתנות לשינוי ב-Java מספקות יתרונות רבים כמו בטיחות חוט ניפוי קל והכל. בג'אווה כל שיעורי עטיפה (כמו Integer Boolean Byte Short) והמחלקה String היא בלתי ניתנת לשינוי. אנחנו יכולים ליצור גם מחלקה בלתי משתנה משלנו.

במאמר זה אנו הולכים ללמוד:

  • מה המשמעות של חוסר שינוי
  • למה זה שימושי
  • איך ליצור מעמד בלתי משתנה משלנו
  • מדוע העתקה עמוקה חשובה
  • מהן המגבלות שיש לסוגי רשומות Java

מהי מחלקה בלתי ניתנת לשינוי?

מחלקה בלתי ניתנת לשינוי היא מחלקה שלא ניתן לשנות את האובייקטים שלה לאחר שנוצרה. אם נעשה שינוי כלשהו זה גורם לאובייקט חדש. שיטה זו משמשת ביישומים במקביל.

כללים ליצירת מחלקה בלתי ניתנת לשינוי

  • יש להכריז על המחלקה כ סוֹפִי כך שלא ניתן ליצור כיתות ילדים.
  • יש להצהיר על חברי הנתונים בכיתה פְּרָטִי כך שאסור גישה ישירה.
  • יש להצהיר על חברי הנתונים בכיתה סוֹפִי כך שלא נוכל לשנות את ערכם לאחר יצירת האובייקט.
  • בנאי בעל פרמטרים צריך לאתחל את כל השדות המבצעים א עותק עמוק כך שלא ניתן לשנות את חברי הנתונים באמצעות הפניה לאובייקט.
  • יש לבצע העתקה עמוקה של אובייקטים בשיטות getter כדי להחזיר עותק במקום להחזיר את הפניה לאובייקט בפועל.

פֶּתֶק : לא צריכים להיות קובעים או במונחים פשוטים יותר לא צריכה להיות אפשרות לשנות את הערך של משתנה המופע.


דוגמה: יישום מחלקה בלתי ניתנת לשינוי

Student.java

Java
   // Java Program to Create An Immutable Class   import     java.util.HashMap  ;   import     java.util.Map  ;   // declare the class as final   final     class   Student     {      // make fields private and final      private     final     String     name  ;      private     final     int     regNo  ;      private     final     Map   <  String       String  >     metadata  ;      // initialize all fields via constructor      public     Student  (  String     name       int     regNo       Map   <  String       String  >     metadata  )     {      this  .  name     =     name  ;      this  .  regNo     =     regNo  ;      // deep copy of mutable object (Map)      Map   <  String       String  >     tempMap     =     new     HashMap   <>  ();      for     (  Map  .  Entry   <  String       String  >     entry     :     metadata  .  entrySet  ())     {      tempMap  .  put  (  entry  .  getKey  ()     entry  .  getValue  ());      }      this  .  metadata     =     tempMap  ;      }      // only provide getters (no setters)      public     String     getName  ()     {      return     name  ;      }      public     int     getRegNo  ()     {      return     regNo  ;      }      // return deep copy to avoid exposing internal state      public     Map   <  String       String  >     getMetadata  ()     {      Map   <  String       String  >     tempMap     =     new     HashMap   <>  ();      for     (  Map  .  Entry   <  String       String  >     entry     :     this  .  metadata  .  entrySet  ())     {      tempMap  .  put  (  entry  .  getKey  ()     entry  .  getValue  ());      }      return     tempMap  ;      }   }   

בדוגמה זו יצרנו מחלקה סופית בשם סטוּדֶנט. יש לו שלושה חברי נתונים סופיים, בנאי עם פרמטרים ושיטות גטר. שימו לב שאין כאן שיטת מגדיר. כמו כן, שים לב שאיננו צריכים לבצע העתקה עמוקה או שיבוט של חברי נתונים מסוגי עטיפה מכיוון שהם כבר בלתי ניתנים לשינוי.

Geeks.java:

Java
   import     java.util.HashMap  ;   import     java.util.Map  ;   public     class   Geeks     {      public     static     void     main  (  String  []     args  )     {      // create a map and adding data      Map   <  String       String  >     map     =     new     HashMap   <>  ();      map  .  put  (  '1'       'first'  );      map  .  put  (  '2'       'second'  );      // create an immutable Student object      Student     s     =     new     Student  (  'GFG'       101       map  );      // accessing data      System  .  out  .  println  (  s  .  getName  ());         System  .  out  .  println  (  s  .  getRegNo  ());         System  .  out  .  println  (  s  .  getMetadata  ());         // try to modify the original map      map  .  put  (  '3'       'third'  );      System  .  out  .  println  (  s  .  getMetadata  ());         // try to modify the map returned by getMetadata()      s  .  getMetadata  ().  put  (  '4'       'fourth'  );      System  .  out  .  println  (  s  .  getMetadata  ());         }   }   

גם לאחר שינוי המפה המקורית או המוחזרת המצב הפנימי של אובייקט הסטודנט נשאר ללא שינוי. זה מאשר את מושג הבלתי משתנה.

תְפוּקָה:

 GFG   
101
{1=first 2=second}
{1=first 2=second}
{1=first 2=second}


הגבלה של רשומת Java עם שדות ניתנים לשינוי

Java 14 הוצג רְשׁוּמָה . זוהי דרך ברורה ותמציתית להגדיר מחלקות כמו בלתי ניתנות לשינוי:

record Student(שם מחרוזת int regNo Map מטא נתונים) {}


אבל זה רק מציע אי-שינוי רדודה. אם המפה משתנה באופן חיצוני, המצב הפנימי של הרשומה משתנה:

מַפָּה מפה = HashMap חדש <>();

map.put('1' 'first');


Student s = new Student('ABC' 101 map);


// משנה מצב פנימי - לא בטוח

map.put('2' 'second');

s.metadata().put('3' 'third');

פֶּתֶק : השתמש ברשומה רק אם כל השדות הם סוגים בלתי ניתנים לשינוי כמו String int או רשומות אחרות.