JavaRush /בלוג Java /Random-HE /מהן גנריות ב-Java

מהן גנריות ב-Java

פורסם בקבוצה
שלום! היום נדבר על תרופות גנריות. אני חייב לומר שתלמדו הרבה דברים חדשים! לא רק זה, אלא גם ההרצאות הבאות יוקדשו לגנריקה. מהן גנריות ב-Java - 1 לכן, אם הנושא הזה מעניין אותך, יש לך מזל: היום תלמד הרבה על תכונות הגנריות. ובכן, אם לא, תירגע והירגע! :) זה נושא חשוב מאוד ואתה צריך לדעת אותו. נתחיל עם אחד פשוט: "מה" ו"למה". מה זה גנרי? גנריות הם טיפוסים עם פרמטר. בעת יצירת חומר גנרי, אתה מציין לא רק את סוגו, אלא גם את סוג הנתונים שהוא אמור לעבוד איתם. אני חושב שהדוגמה הכי ברורה כבר עלתה בראשכם - זו ArrayList! כך אנחנו בדרך כלל יוצרים את זה בתוכנית:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
כפי שאתה יכול לנחש, המוזרות של הרשימה היא שלא ניתן יהיה "לתפוס" הכל לתוכה: היא פועלת אך ורק עם חפצים String. עכשיו בואו נצא לסיור קצר בהיסטוריה של ג'אווה וננסה לענות על השאלה: "למה?" לשם כך, אנו בעצמנו נכתוב גרסה פשוטה של ​​המחלקה ArrayList. הרשימה שלנו יכולה רק להוסיף נתונים למערך הפנימי ולקבל נתונים אלה:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
נניח שאנו רוצים שהרשימה שלנו תשמור רק מספרים Integer. אין לנו תרופות גנריות. איננו יכולים לציין במפורש את מופע o של בדיקה Integerב- add(). אז כל המחלקה שלנו תתאים רק עבור Integer, ונצטרך לכתוב את אותה מחלקה עבור כל סוגי הנתונים הקיימים בעולם! אנחנו מחליטים להסתמך על המתכנתים שלנו ופשוט להשאיר הערה בקוד כדי שלא יוסיפו שם שום דבר מיותר:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
אחד המתכנתים פספס את ההערה הזו וניסה בשוגג לשים מספרים מעורבבים עם מחרוזות ברשימה, ואז לחשב את הסכום שלהם:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
פלט מסוף: 300 חריגה בשרשור "ראשי" java.lang.ClassCastException: לא ניתן להטיל java.lang.String ל-java.lang.Integer ב-Main.main(Main.java:14) מה הכי גרוע במצב הזה? רחוק מלהיות חוסר תשומת לב של מתכנת. הדבר הגרוע ביותר הוא שהקוד השגוי הגיע למקום חשוב בתוכנית שלנו והודר בהצלחה . כעת נראה את השגיאה לא בשלב הקידוד, אלא רק בשלב הבדיקה (וזה במקרה הטוב!). תיקון באגים מאוחר יותר בפיתוח עולה הרבה יותר - גם כסף וגם זמן. זה בדיוק היתרון של גנריות: מחלקה גנרית תאפשר למתכנת חסר מזל לזהות שגיאה באופן מיידי. הקוד פשוט לא יעשה קומפילציה!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//error!
       myList1.add("Shalala");//error!
   }
}
המתכנת מיד "יתעשת" ויתקן את עצמו באופן מיידי. אגב, לא היינו צריכים ליצור מחלקה משלנו Listכדי לראות שגיאות מסוג זה. פשוט הסר את סוגריים בסוגריים ( <Integer>) מ-ArrayList רגיל!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
פלט מסוף: 300 חריגה בשרשור "ראשי" java.lang.ClassCastException: לא ניתן להטיל את java.lang.String ל-java.lang.Integer ב-Main.main(Main.java:16) כלומר, אפילו באמצעות כלים "מקוריים" Java, אתה יכול לעשות את הטעות הזו וליצור אוסף לא בטוח. עם זאת, אם נדביק את הקוד הזה לתוך IDEa, אנו רואים אזהרה: " קריאה לא מסומנת להוסיף (E) כחבר בסוג הגולמי של java.util.List " זה אומר לנו שמשהו עלול להשתבש בעת הוספת אלמנט ל- אוסף ללא תרופות גנריות לא כך. אבל מה פירוש הביטוי "סוג גולמי"? התרגום המילולי יהיה די מדויק - " סוג גולמי " או " סוג מלוכלך ". Raw typeהיא מחלקה גנרית שהסוג שלה הוסר ממנה. במילים אחרות, List myList1זהו Raw type. ההפך raw typeהוא generic typeמחלקה גנרית (המכונה גם מחלקה parameterized type), שנוצרה בצורה נכונה, עם מפרט סוג. לדוגמה, List<String> myList1. אולי יש לך שאלה: למה בכלל מותר להשתמש raw types? הסיבה פשוטה. יוצרי Java השאירו תמיכה בשפה raw typesכדי לא ליצור בעיות תאימות. בזמן ש-Java 5.0 שוחרר (גנריקה הופיעה בפעם הראשונה בגרסה זו), הרבה קוד כבר נכתב באמצעות raw types. לכן, אפשרות זו קיימת עד היום. כבר הזכרנו יותר מפעם אחת בהרצאות את ספרו הקלאסי של יהושע בלוך "ג'אווה יעילה". כאחד מיוצרי השפה, הוא לא התעלם מנושא השימוש raw typesובספר generic types. מהן גנריות ב-Java - 2לפרק 23 של הספר הזה יש כותרת מאוד רהוטה: "אל תשתמש בסוגים גולמיים בקוד חדש." זה משהו שאתה צריך לזכור. בעת שימוש במחלקות גנריות, לעולם אל תהפוך אותן generic typeל raw type.

שיטות מוקלדות

Java מאפשרת לך להקליד שיטות בודדות, וליצור מה שנקרא שיטות גנריות. למה שיטות כאלה נוחות? קודם כל, כי הם מאפשרים לך לעבוד עם סוגים שונים של פרמטרים. אם ניתן ליישם את אותו היגיון בבטחה על סוגים שונים, שיטה גנרית היא פתרון מצוין. בואו נסתכל על דוגמה. נניח שיש לנו איזושהי רשימה myList1. אנחנו רוצים להסיר ממנו את כל הערכים ולמלא את כל החללים הפנויים בערך חדש. כך תיראה הכיתה שלנו עם שיטה גנרית:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
שימו לב לתחביר, זה נראה קצת יוצא דופן:
public static <T> void fill(List<T> list, T val)
לפני סוג ההחזרה מופיע <T>, המציין שיטה גנרית. במקרה זה, השיטה לוקחת 2 פרמטרים כקלט: רשימה של אובייקטים T ועוד אובייקט נפרד T. על ידי שימוש ב-<T> מושגת הקלדת השיטה: לא נוכל להעביר לשם רשימה של מחרוזות ומספר. רשימה של מחרוזות ומחרוזת, רשימה של מספרים ומספר, רשימה של החפצים שלנו Catועוד חפץ Cat- זו הדרך היחידה. השיטה main()מדגימה בבירור שהשיטה fill()עובדת בקלות עם סוגי נתונים שונים. ראשית, הוא מקבל כקלט רשימה של מחרוזות ומחרוזת, ולאחר מכן רשימה של מספרים ומספר. פלט מסוף: [Newline, Newline, Newline] [888, 888, 888] תאר לעצמך אם fill()היינו צריכים לוגיקה של מתודה עבור 30 מחלקות שונות, ולא היו לנו שיטות גנריות. נאלץ לכתוב את אותה שיטה 30 פעמים, רק עבור סוגי נתונים שונים! אבל הודות לשיטות גנריות, אנחנו יכולים לעשות שימוש חוזר בקוד שלנו! :)

שיעורים מוקלדים

אתה יכול לא רק להשתמש במחלקות הגנריות הניתנות ב-Java, אלא גם ליצור משלך! הנה דוגמה פשוטה:
public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
הכיתה שלנו Box<T>("קופסה") מוקלדת. לאחר שהקצינו לו סוג נתונים ( ) במהלך היצירה <T>, לא נוכל יותר למקם בו אובייקטים מסוגים אחרים. ניתן לראות זאת בדוגמה. בעת היצירה, ציינו שהאובייקט שלנו יעבוד עם מחרוזות:
Box<String> stringBox = new Box<>();
וכאשר בשורת הקוד האחרונה אנחנו מנסים לשים את המספר 12345 בתוך התיבה, נקבל שגיאת קומפילציה! בדיוק ככה, יצרנו מחלקה גנרית משלנו! :) זה מסיים את ההרצאה שלנו היום. אבל אנחנו לא נפרדים מהגנריות! בהרצאות הבאות נדבר על תכונות מתקדמות יותר, אז אל תיפרדו! ) בהצלחה בלימודים! :)
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION