سلام! در درس امروز به مطالعه ژنریک ها ادامه خواهیم داد. اتفاقاً این یک موضوع بزرگ است ، اما جایی برای رفتن وجود ندارد - این بخش بسیار مهمی از زبان است :) وقتی اسناد Oracle را در مورد ژنریک مطالعه می کنید یا راهنماها را در اینترنت می خوانید ، با اصطلاحات روبرو می شوید. انواع غیر قابل اصلاح و انواع قابل تجدید . "قابل اصلاح" چه نوع کلمه ای است؟ حتی اگر همه چیز با زبان انگلیسی خوب باشد، بعید است که با آن آشنا شده باشید. بیایید سعی کنیم ترجمه کنیم!
*با تشکر از شما گوگل، شما کمک زیادی کردید -_-*
Reifiable-type نوعی است که اطلاعات آن در زمان اجرا کاملاً در دسترس است. در زبان جاوا، اینها شامل انواع اولیه، نوع خام و انواع غیر عمومی است. در مقابل، Non-Reifiable Types انواعی هستند که اطلاعات آنها در زمان اجرا پاک شده و از دسترس خارج می شود. اینها فقط عمومی هستند - List<String> ، List<Integer> و غیره.
به هر حال، آیا به یاد دارید که varargs چیست ؟
اگر فراموش کردید، این آرگومانهای طول متغیر هستند. آنها در شرایطی مفید هستند که ما دقیقاً نمی دانیم چه تعداد استدلال می تواند به روش ما منتقل شود. به عنوان مثال، اگر یک کلاس ماشین حساب داشته باشیم و یک متد داشته باشد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>
E
main()
addAll()
List
List
Pair<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>) پاک خواهد شد. ایجاد آرایه ها از نوع غیر قابل تکرار در جاوا مجاز نیست . اگر بخواهید به صورت دستی یک آرایه جفت<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
ژنریک ها با هم، آلودگی هیپ است. آلودگی ممکن است در شرایط زیر رخ دهد:
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);
}
}
اینجا چه خبره؟ به دلیل پاک کردن نوع، برگه های پارامتر ما (برای راحتی، آنها را به جای «فهرست ها» «sheets» می نامیم) -
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
:) و اینجاست که سخنرانی امروز ما به پایان می رسد. حل چند مشکل را فراموش نکنید و اگر وقت و انرژی دارید، ادبیات اضافی مطالعه کنید. " جاوا موثر " خود را نمی خواند! :) به امید دیدار!
GO TO FULL VERSION