CodeGym /مدونة جافا /Random-AR /الأدوية العامة في جافا
John Squirrels
مستوى
San Francisco

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

نشرت في المجموعة
أهلاً! سنتحدث عن Java Generics. يجب أن أقول أنك سوف تتعلم الكثير! ولن يتم تخصيص هذا الدرس فقط، ولكن أيضًا الدروس القليلة القادمة، للأدوية الجنيسة. لذا، إذا كنت مهتمًا بالأدوية العامة، فأنت اليوم محظوظ: ستتعلم الكثير عن ميزات الأدوية العامة. وإذا لم يكن الأمر كذلك، استقال واسترخي! :) هذا موضوع مهم للغاية، وتحتاج إلى معرفته. لنبدأ بالبساطة: "ماذا" و"لماذا".

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

الأدوية العامة هي الأنواع التي لها معلمة. عند إنشاء نوع عام، فإنك لا تحدد النوع فحسب، بل تحدد أيضًا نوع البيانات الذي سيعمل معه. أعتقد أن المثال الأكثر وضوحًا قد تبادر إلى ذهنك بالفعل: 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");
   }
}
كما قد تتخيل، إحدى ميزات هذه القائمة هي أننا لا نستطيع إدخال كل شيء فيها: فهي تعمل حصريًا مع كائنات السلسلة . الآن دعونا نلقي نظرة سريعة على تاريخ جافا ونحاول الإجابة على السؤال "لماذا؟" للقيام بذلك، سنكتب نسختنا المبسطة من فئة 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;
   }
}
لنفترض أننا نريد أن تقوم قائمتنا بتخزين الأعداد الصحيحة فقط . نحن لا نستخدم نوعًا عامًا. لا نريد تضمين فحص "مثيل لعدد صحيح " بشكل صريح في طريقة add() . إذا فعلنا ذلك، فسيكون فصلنا بأكمله مناسبًا فقط لـ Integer ، وسيتعين علينا كتابة فصل مماثل لكل أنواع البيانات الأخرى في العالم! سنعتمد على المبرمجين لدينا، ونترك تعليقًا في الكود للتأكد من أنهم لن يضيفوا أي شيء لا نريده:

// Use this class ONLY with the 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 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
      at 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!
   }
}
يدرك المبرمج خطأه على الفور ويتحسن على الفور. بالمناسبة، لم يكن علينا إنشاء فئة قائمة خاصة بنا لرؤية هذا النوع من الأخطاء. ما عليك سوى إزالة الأقواس الزاوية واكتب ( <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 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
     at Main.main(Main.java:16)
بمعنى آخر، حتى باستخدام آليات Java "الأصلية"، يمكننا ارتكاب هذا النوع من الأخطاء وإنشاء مجموعة غير آمنة. ومع ذلك، إذا قمنا بلصق هذا الرمز في IDE، فسنحصل على تحذير: "استدعاء لم يتم التحقق منه لإضافة (E) كعضو في النوع الأولي من java.util.List" تم إخبارنا أنه قد يحدث خطأ ما عند إضافة عنصر إلى مجموعة تفتقر إلى نوع عام. ولكن ماذا تعني عبارة "النوع الخام"؟ النوع الخام هو فئة عامة تمت إزالة نوعها. بمعنى آخر، List myList1 هو نوع خام . عكس النوع الخام هو النوع العام - فئة عامة مع إشارة إلى النوع (الأنواع) ذات المعلمات . على سبيل المثال، List<String> myList1 . قد تتساءل لماذا تسمح اللغة باستخدام الأنواع الخام ؟ السبب بسيط. ترك منشئو Java الدعم للأنواع الأولية في اللغة لتجنب خلق مشكلات التوافق. بحلول الوقت الذي تم فيه إصدار Java 5.0 (ظهرت الأدوية العامة لأول مرة في هذا الإصدار)، كان الكثير من التعليمات البرمجية قد تمت كتابته بالفعل باستخدام الأنواع الأولية . ونتيجة لذلك، لا تزال هذه الآلية مدعومة حتى اليوم. لقد ذكرنا مرارًا وتكرارًا كتاب جوشوا بلوخ الكلاسيكي "جافا الفعالة" في الدروس. وباعتباره أحد مبدعي اللغة، لم يتخطى الأنواع الأولية والأنواع العامة في كتابه. ما هي الأدوية العامة في جافا؟  - 2يحتوي الفصل 23 من الكتاب على عنوان بليغ للغاية: "لا تستخدم الأنواع الأولية في التعليمات البرمجية الجديدة" وهذا ما تحتاج إلى تذكره. عند استخدام الفئات العامة، لا تقم أبدًا بتحويل النوع العام إلى نوع خام .

طرق عامة

تتيح لك 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("Old String 1");
       strings.add("Old String 2");
       strings.add("Old String 3");

       fill(strings, "New String");

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

[New String, New String, New String] [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("Old String");
       System.out.println(stringBox.get());
       stringBox.set("New String");

       System.out.println(stringBox.get());
      
       stringBox.set(12345); // Compilation error!
   }
}
فئة Box<T> الخاصة بنا هي فئة عامة. بمجرد تعيين نوع البيانات ( <T> ) أثناء الإنشاء، لم يعد بإمكاننا وضع كائنات من أنواع أخرى فيه. ويمكن رؤية هذا في المثال. عند إنشاء الكائن، أشرنا إلى أنه سيعمل مع السلاسل النصية:

Box<String> stringBox = new Box<>();
وفي السطر الأخير من الكود، عندما نحاول وضع الرقم 12345 داخل الصندوق، نحصل على خطأ في الترجمة! انه من السهل! لقد أنشأنا فئة عامة خاصة بنا! :) وبهذا ينتهي درس اليوم. لكننا لا نقول وداعا للأدوية الجنيسة! في الدروس القادمة، سنتحدث عن المزيد من الميزات المتقدمة، فلا تذهب بعيدًا! ) لتعزيز ما تعلمته، نقترح عليك مشاهدة درس فيديو من دورة Java الخاصة بنا
أفضل النجاح في دراستك! :)
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION