JavaRush /בלוג Java /Random-HE /שימוש ב-varargs בעבודה עם תרופות גנריות

שימוש ב-varargs בעבודה עם תרופות גנריות

פורסם בקבוצה
שלום! בשיעור של היום נמשיך ללמוד גנריקה. במקרה זה נושא גדול, אבל אין לאן ללכת - זה חלק חשוב ביותר בשפה :) כאשר תלמדו את התיעוד של אורקל על גנריות או תקראו מדריכים באינטרנט, תיתקלו במונחים סוגים שאינם ניתנים לריפוי וסוגים ניתנים לתיקון . איזו מילה היא "ניתנת לריפי"? גם אם הכל טוב עם אנגלית, לא סביר שפגשת אותה. בואו ננסה לתרגם! שימוש ב-varargs בעבודה עם תרופות גנריות - 2
*תודה גוגל, עזרת מאוד -_-*
Reifiable-type הוא סוג שהמידע שלו זמין במלואו בזמן הריצה. בשפת ג'אווה, אלה כוללים פרימיטיבים, טיפוסים גולמיים וסוגים לא-גנריים. לעומת זאת, סוגים שאינם ניתנים לריפוי הם טיפוסים שהמידע שלהם נמחק והופך ללא זמין בזמן ריצה. אלו הם רק כלליים - List<String> , List<Integer> וכו'.

אגב, אתה זוכר מה זה וארגים ?

למקרה ששכחת, אלו הם ארגומנטים באורך משתנה. הם שימושיים במצבים בהם איננו יודעים בדיוק כמה טיעונים ניתן להעביר לשיטתנו. לדוגמה, אם יש לנו מחלקת מחשבון ויש לה שיטה sum. sum()אתה יכול להעביר 2 מספרים, 3, 5, או כמה שאתה רוצה לשיטה . זה יהיה מאוד מוזר להעמיס על השיטה בכל פעם sum()כדי לקחת בחשבון את כל האפשרויות האפשריות. במקום זאת נוכל לעשות זאת:
public class SimpleCalculator {

   public static int sum(int...numbers) {

       int result = 0;

       for(int i : numbers) {

           result += i;
       }

       return result;
   }

   public static void main(String[] args) {

       System.out.println(sum(1,2,3,4,5));
       System.out.println(sum(2,9));
   }
}
פלט מסוף:

15
11
לכן, כאשר משתמשים varargsבשילוב עם תרופות גנריות יש כמה תכונות חשובות. בואו נסתכל על הקוד הזה:
import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static <E> void addAll(List<E> list, E... array) {

       for (E element : array) {
           list.add(element);
       }
   }

   public static void main(String[] args) {
       addAll(new ArrayList<String>(),  //  здесь все нормально
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // а здесь мы получаем предупреждение
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
השיטה לוקחת רשימה וכל מספר אובייקטים addAll()כקלט , ולאחר מכן מוסיפה את כל האובייקטים הללו לרשימה. בשיטה אנו קוראים לשיטה שלנו פעמיים . בפעם הראשונה אנחנו מוסיפים שתי שורות רגילות. הכל בסדר כאן. בפעם השנייה נוסיף שני אובייקטים . והנה פתאום אנחנו מקבלים אזהרה: List<E>Emain()addAll()ListListPair<String, String>

Unchecked generics array creation for varargs parameter
מה זה אומר? למה אנחנו מקבלים אזהרה ומה זה קשור לזה array? Array- זהו מערך, ואין מערכים בקוד שלנו! נתחיל עם השני. האזהרה מזכירה מערך מכיוון שהמהדר ממיר ארגומנטים באורך משתנה (varargs) למערך. במילים אחרות, החתימה של השיטה שלנו היא addAll():
public static <E> void addAll(List<E> list, E... array)
למעשה זה נראה כך:
public static <E> void addAll(List<E> list, E[] array)
כלומר, בשיטה main(), המהדר ימיר את הקוד שלנו לזה:
public static void main(String[] args) {
   addAll(new ArrayList<String>(),
      new String[] {
        "Leonardo da Vinci",
        "Vasco de Gama"
      }
   );
   addAll(new ArrayList<Pair<String,String>>(),
        new Pair<String,String>[] {
            new Pair<String,String>("Leonardo","da Vinci"),
            new Pair<String,String>("Vasco","de Gama")
        }
   );
}
הכל בסדר עם המערך String. אבל עם מערך Pair<String, String>- לא. העובדה היא Pair<String, String>שזה סוג שאינו ניתן לריפוי. במהלך ההידור, כל המידע על סוגי פרמטרים (<String, String>) יימחק. יצירת מערכים מסוג Non-Reifiable אינה מותרת ב-Java . אתה יכול לאמת זאת אם תנסה ליצור באופן ידני מערך Pair<String, String>
public static void main(String[] args) {

   //  ошибка компиляции! Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
הסיבה ברורה - סוג בטיחות. כזכור, בעת יצירת מערך, עליך לציין אילו אובייקטים (או פרימיטיביים) מערך זה יאחסן.
int array[] = new int[10];
באחד השיעורים הקודמים בדקנו בפירוט את מנגנון מחיקת הסוג. אז, במקרה זה, כתוצאה ממחיקת טיפוסים, איבדנו את המידע שצמדים Pairאוחסנו באובייקטים שלנו <String, String>. יצירת מערך תהיה לא בטוחה. בעת שימוש בשיטות עם varargsוגנריות, הקפד לזכור לגבי מחיקת סוג וכיצד זה עובד בדיוק. אם אתה בטוח לחלוטין בקוד שכתבת, ואתה יודע שהוא לא יגרום לבעיות, אתה יכול לבטל varargsאזהרות הקשורות אליו באמצעות הערה@SafeVarargs
@SafeVarargs
public static <E> void addAll(List<E> list, E... array) {

   for (E element : array) {
       list.add(element);
   }
}
אם תוסיף את ההערה הזו לשיטה שלך, האזהרה שנתקלנו בה קודם לא תופיע. בעיה אפשרית נוספת בעת שימוש varargsגנרי יחד הוא זיהום ערמות. שימוש ב-varargs בעבודה עם תרופות גנריות - 4זיהום יכול להתרחש במצב הבא:
import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> makeHeapPollution() {
       List numbers = new ArrayList<Number>();
       numbers.add(1);
       List<String> strings = numbers;
       strings.add("");
       return strings;
   }

   public static void main(String[] args) {

       List<String> stringsWithHeapPollution = makeHeapPollution();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
פלט מסוף:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
במילים פשוטות, זיהום ערימה הוא מצב שבו חפצים מסוג 1 צריכים להיות על הערימה А, אך חפצים מסוג זה מגיעים לשם B, עקב שגיאות בטיחות מסוג. בדוגמה שלנו זה מה שקורה. ראשית יצרנו משתנה Raw numbersוהקצינו אותו לאוסף גנרי ArrayList<Number>. אחרי זה הוספנו שם את המספר 1.
List<String> strings = numbers;
בשורה זו, המהדר ניסה להזהיר אותנו מפני שגיאות אפשריות על ידי הוצאת האזהרה " הקצאה לא מסומנת... ", אך התעלמנו ממנה. כתוצאה מכך, יש לנו משתנה גנרי מסוג List<String>המצביע על אוסף גנרי של סוג ArrayList<Number>. מצב זה יכול להוביל בבירור לצרות! זה מה שקורה. באמצעות המשתנה החדש שלנו, אנו מוסיפים מחרוזת לאוסף. הערימה הייתה מזוהמת - הוספנו תחילה מספר ואחר כך מחרוזת לאוסף המוקלד. המהדר הזהיר אותנו, אבל התעלמנו מהאזהרה שלו, וקיבלנו תוצאות ClassCastExceptionרק בזמן שהתוכנית פועלת. מה זה קשור varargs? שימוש varargsעם תרופות גנריות יכול בקלות להוביל לזיהום ערימה. הנה דוגמה פשוטה:
import java.util.Arrays;
import java.util.List;

public class Main {

   static void makeHeapPollution(List<String>... stringsLists) {
       Object[] array = stringsLists;
       List<Integer> numbersList = Arrays.asList(66,22,44,12);

       array[0] = numbersList;
       String str = stringsLists[0].get(0);
   }

   public static void main(String[] args) {

       List<String> cars1 = Arrays.asList("Ford", "Fiat", "Kia");
       List<String> cars2 = Arrays.asList("Ferrari", "Bugatti", "Zaporozhets");

       makeHeapPollution(cars1, cars2);
   }
}
מה קורה פה? עקב מחיקת סוג, גיליונות הפרמטרים שלנו (נקראו להם "גליונות" במקום "רשימות" מטעמי נוחות) הם -
List<String>...stringsLists
- יהפוך למערך של גיליונות - List[]עם סוג לא ידוע (אל תשכח ש-varargs הופך למערך רגיל כתוצאה מהקומפילציה). בשל כך, אנו יכולים בקלות לבצע הקצאה למשתנה Object[] arrayבשורה הראשונה של השיטה – הטיפוסים נמחקו מהגליונות שלנו! ועכשיו יש לנו משתנה מסוג Object[], שבו אנחנו יכולים להוסיף כל דבר בכלל - כל האובייקטים ב-Java יורשים מ- Object! כרגע יש לנו רק מערך של דפי מחרוזת. אבל הודות לשימוש varargsומחיקה של סוגים, אנחנו יכולים בקלות להוסיף להם גיליון של מספרים, וזה מה שאנחנו עושים. כתוצאה מכך, אנו מזהמים את הערימה על ידי ערבוב חפצים מסוגים שונים. התוצאה תהיה אותה חריגה ClassCastExceptionכאשר מנסים לקרוא מחרוזת מהמערך. פלט מסוף:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
אלו ההשלכות הבלתי צפויות שיכולות לנבוע משימוש במנגנון פשוט לכאורה varargs:) וכאן מסתיימת ההרצאה שלנו היום. אל תשכח לפתור כמה בעיות, ואם נשאר לך זמן ואנרגיה, למד ספרות נוספת. " Java יעיל " לא יקרא את עצמו! :) נתראה!
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION