CodeGym /وبلاگ جاوا /Random-FA /استفاده از varargs هنگام کار با ژنریک
John Squirrels
مرحله
San Francisco

استفاده از varargs هنگام کار با ژنریک

در گروه منتشر شد
سلام! در درس امروز، ما به مطالعه ژنریک ها ادامه خواهیم داد. همانطور که اتفاق می افتد، این یک موضوع بزرگ است، اما نمی توان از آن اجتناب کرد - این بخش بسیار مهمی از زبان است :) هنگامی که اسناد اوراکل را در مورد ژنریک مطالعه می کنید یا آموزش های آنلاین را مطالعه می کنید، با اصطلاحات انواع غیرقابل تکرار و انواع قابل اصلاح . نوع قابل تایید نوعی است که اطلاعات آن در زمان اجرا به طور کامل در دسترس است. در جاوا، چنین انواعی شامل انواع اولیه، انواع خام و انواع غیر عمومی است. در مقابل، انواع غیرقابل اصلاح ، انواعی هستند که اطلاعات آنها پاک می شود و در زمان اجرا غیرقابل دسترسی می شود. همانطور که اتفاق می افتد، اینها عمومی هستند - 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
این به ما نشان می‌دهد که هنگام استفاده از vararg در ترکیب با ژنریک، ویژگی‌های مهمی وجود دارد. بیایید به کد زیر نگاه کنیم:

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>(), // This is okay
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // but here we get a warning
       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()ListPair<String, String>List

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>) پاک می شود. ایجاد آرایه‌هایی از نوع غیرقابل تکرار در جاوا مجاز نیست . اگر بخواهید به صورت دستی یک آرایه Pair<String, String> ایجاد کنید، می توانید این را ببینید

public static void main(String[] args) {

   // Compilation error 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);
   }
}
اگر این حاشیه نویسی را به روش خود اضافه کنید، هشداری که قبلاً با آن مواجه شدیم ظاهر نمی شود. یکی دیگر از مشکلاتی که هنگام استفاده از vararg با ژنریک ممکن است رخ دهد، آلودگی هیپ است. استفاده از varargs هنگام کار با ژنریک - 3آلودگی هیپ می تواند در شرایط زیر رخ دهد:

import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> polluteHeap() {
       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 = polluteHeap();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
خروجی کنسول:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
به زبان ساده، آلودگی هیپ زمانی است که اشیاء از نوع Aباید در پشته باشند، اما اشیاء از نوع Bبه دلیل خطاهای مربوط به ایمنی نوع به آنجا ختم می‌شوند. در مثال ما، این دقیقاً همان چیزی است که اتفاق می افتد. ابتدا numbersمتغیر خام را ایجاد کردیم و یک مجموعه عمومی ( ArrayList<Number>) به آن اختصاص دادیم. سپس شماره را 1به مجموعه اضافه کردیم.

List<String> strings = numbers;
در این خط، کامپایلر سعی کرد با اخطار « تخصیص بررسی نشده... » ما را نسبت به خطاهای احتمالی هشدار دهد، اما ما آن را نادیده گرفتیم. ما در نهایت با یک متغیر عمومی از نوع مواجه می شویم List<String>که به مجموعه ای عمومی از نوع اشاره می کند ArrayList<Number>. واضح است که این وضعیت می تواند منجر به دردسر شود! و همینطور هم می شود. با استفاده از متغیر جدید خود، یک رشته به مجموعه اضافه می کنیم. ما اکنون آلودگی هیپ داریم - یک عدد و سپس یک رشته به مجموعه پارامتری شده اضافه کردیم. کامپایلر به ما هشدار داد، اما ما به هشدار آن توجه نکردیم. در نتیجه، ClassCastExceptionفقط زمانی که برنامه در حال اجرا است، یک عدد دریافت می کنیم. پس این چه ربطی به varargs دارد؟ استفاده از vararg با ژنریک می تواند به راحتی منجر به آلودگی پشته شود. در اینجا یک مثال ساده آورده شده است:

import java.util.Arrays;
import java.util.List;

public class Main {

   static void polluteHeap(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");

       polluteHeap(cars1, cars2);
   }
}
اینجا چه خبره؟ به دلیل پاک کردن نوع، آرگومان طول متغیر ما

List<String>...stringsLists
به آرایه ای از لیست ها تبدیل می شود، به عنوان مثال List[]، از اشیاء از نوع ناشناخته (فراموش نکنید که varargs در طول کامپایل به یک آرایه منظم تبدیل می شود). به همین دلیل، ما به راحتی می توانیم آن را به Object[] arrayمتغیر خط اول متد اختصاص دهیم - نوع اشیاء موجود در لیست های ما پاک شده است! و اکنون یک متغیر داریم Object[]که می‌توانیم هر چیزی را به آن اضافه کنیم، زیرا همه اشیاء در جاوا ارث می‌برند Object! در ابتدا، ما فقط آرایه ای از لیست های رشته ها را داریم. اما به لطف نوع پاک کردن و استفاده ما از varargs، ما به راحتی می توانیم لیستی از اعداد را اضافه کنیم، که انجام می دهیم. در نتیجه با مخلوط کردن اجسام از انواع مختلف، کپه را آلوده می کنیم. ClassCastExceptionهنگامی که ما سعی می کنیم یک رشته را از آرایه بخوانیم، نتیجه دیگری خواهد بود . خروجی کنسول:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
چنین پیامدهای غیرمنتظره ای می تواند با استفاده از varargs، یک مکانیسم به ظاهر ساده ایجاد شود :) و با آن، درس امروز به پایان می رسد. فراموش نکنید که چند کار را حل کنید و اگر وقت و انرژی دارید، مقداری مطالعه اضافی مطالعه کنید. " جاوای موثر " خود را نمی خواند! :) تا دفعه بعد!
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION