JavaRush /مدونة جافا /Random-AR /ما هي الأدوية العامة في جافا

ما هي الأدوية العامة في جافا

نشرت في المجموعة
مرحبًا! اليوم سنتحدث عن الأدوية الجنيسة. يجب أن أقول أنك سوف تتعلم الكثير من الأشياء الجديدة! ليس هذا فحسب، بل سيتم أيضًا تخصيص المحاضرات القليلة القادمة للأدوية الجنيسة. ما هي الأدوية العامة في جافا - 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. ما هي الأدوية العامة في جافا - 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>، مما يشير إلى طريقة عامة. في هذه الحالة، تأخذ الطريقة معلمتين كمدخلات: قائمة الكائنات 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