JavaRush /مدونة جافا /Random-AR /الأدوية العامة للقطط
Viacheslav
مستوى

الأدوية العامة للقطط

نشرت في المجموعة
الأدوية العامة للقطط - 1

مقدمة

اليوم هو يوم عظيم لتذكر ما نعرفه عن جافا. وفقا للوثيقة الأكثر أهمية، أي. مواصفات لغة جافا (JLS - مواصفات لغة جافا)، جافا هي لغة مكتوبة بقوة، كما هو موضح في الفصل " الفصل الرابع. الأنواع والقيم والمتغيرات ". ماذا يعني هذا؟ لنفترض أن لدينا طريقة رئيسية:
public static void main(String[] args) {
String text = "Hello world!";
System.out.println(text);
}
تضمن الكتابة القوية أنه عند تجميع هذا الرمز، سيتحقق المترجم من أنه إذا حددنا نوع متغير النص كسلسلة، فإننا لا نحاول استخدامه في أي مكان كمتغير من نوع آخر (على سبيل المثال، كعدد صحيح) . على سبيل المثال، إذا حاولنا حفظ قيمة بدلاً من النص 2L(أي طويلة بدلاً من سلسلة)، فسوف نحصل على خطأ في وقت الترجمة:

Main.java:3: error: incompatible types: long cannot be converted to String
String text = 2L;
أولئك. تسمح لك الكتابة القوية بالتأكد من تنفيذ العمليات على الكائنات فقط عندما تكون هذه العمليات قانونية لتلك الكائنات. وهذا ما يسمى أيضًا أمان النوع. كما هو مذكور في JLS، هناك فئتان من الأنواع في Java: الأنواع البدائية والأنواع المرجعية. يمكنك أن تتذكر الأنواع البدائية من مقالة المراجعة: " الأنواع البدائية في Java: إنها ليست بدائية جدًا ." يمكن تمثيل أنواع المراجع بواسطة فئة أو واجهة أو صفيف. واليوم سنكون مهتمين بالأنواع المرجعية. ولنبدأ بالمصفوفات:
class Main {
  public static void main(String[] args) {
    String[] text = new String[5];
    text[0] = "Hello";
  }
}
يعمل هذا الرمز دون خطأ. كما نعلم (على سبيل المثال، من " Oracle Java Tutorial: Arrays ")، المصفوفة عبارة عن حاوية تقوم بتخزين البيانات من نوع واحد فقط. في هذه الحالة - خطوط فقط. دعونا نحاول إضافة فترة طويلة إلى المصفوفة بدلاً من السلسلة:
text[1] = 4L;
لنقم بتشغيل هذا الرمز (على سبيل المثال، في Repl.it Online Java Compiler ) وسنحصل على خطأ:
error: incompatible types: long cannot be converted to String
لم يسمح لنا المصفوفة وسلامة الكتابة في اللغة بحفظ ما لا يناسب النوع في مصفوفة. هذا مظهر من مظاهر سلامة النوع. قيل لنا: "أصلحوا الخطأ، لكن حتى ذلك الحين لن أقوم بتجميع الكود". والأهم من ذلك أن هذا يحدث أثناء التجميع وليس عند إطلاق البرنامج. أي أننا نرى الأخطاء فورًا، وليس «يومًا ما». وبما أننا تذكرنا المصفوفات، فلنتذكر أيضًا Java Collections Framework . كان لدينا هياكل مختلفة هناك. على سبيل المثال، القوائم. لنعيد كتابة المثال:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List text = new ArrayList(5);
    text.add("Hello");
    text.add(4L);
    String test = text.get(0);
  }
}
عند تجميعه، سنتلقى testخطأ في سطر تهيئة المتغير:
incompatible types: Object cannot be converted to String
في حالتنا، يمكن للقائمة تخزين أي كائن (أي كائن من النوع Object). لذلك يقول المترجم إنه لا يستطيع تحمل مثل هذا العبء من المسؤولية. لذلك، نحتاج إلى تحديد النوع الذي سنحصل عليه من القائمة بشكل صريح:
String test = (String) text.get(0);
يُطلق على هذا المؤشر اسم تحويل النوع أو صب النوع. وسيعمل كل شيء بشكل جيد الآن حتى نحاول الحصول على العنصر في الفهرس 1، لأنه وهو من النوع الطويل. وسوف نحصل على خطأ عادل، ولكن بالفعل أثناء تشغيل البرنامج (في وقت التشغيل):

type conversion, typecasting
Exception in thread "main" java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
كما نرى، هناك العديد من العيوب الهامة هنا. أولاً، نحن مضطرون إلى "إرسال" القيمة التي تم الحصول عليها من القائمة إلى فئة السلسلة. أوافق، هذا قبيح. ثانيا في حالة وجود خطأ لن نراه إلا عند تنفيذ البرنامج. إذا كانت التعليمات البرمجية الخاصة بنا أكثر تعقيدًا، فقد لا نكتشف مثل هذا الخطأ على الفور. وبدأ المطورون في التفكير في كيفية جعل العمل في مثل هذه المواقف أسهل والكود أكثر وضوحًا. وقد ولدوا - الجينات.
الأدوية العامة للقطط - 2

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

لذلك، الأدوية العامة. ما هذا؟ النوع العام هو طريقة خاصة لوصف الأنواع المستخدمة، والتي يمكن لمترجم الكود استخدامها في عمله لضمان سلامة النوع. يبدو شيء من هذا القبيل:
الأدوية العامة للقطط - 3
فيما يلي مثال قصير وشرح:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> text = new ArrayList<String>(5);
    text.add("Hello");
    text.add(4L);
    String test = text.get(1);
  }
}
في هذا المثال، نقول إن لدينا ليس فقط List، ولكن List، والذي يعمل فقط مع كائنات من النوع String. وليس غيرها. ما هو مبين بين قوسين، يمكننا تخزينه. تسمى هذه "الأقواس" "أقواس زاوية" ، أي. بين قوسين زاوية. سيتفضل المترجم بالتحقق مما إذا كنا قد ارتكبنا أي أخطاء عند العمل مع قائمة السلاسل (تسمى القائمة بالنص). سيرى المترجم أننا نحاول بوقاحة وضع Long في قائمة String. وفي وقت التجميع سيعطي خطأ:
error: no suitable method found for add(long)
ربما تكون قد تذكرت أن String هو سليل CharSequence. وقرر أن تفعل شيئًا مثل:
public static void main(String[] args) {
	ArrayList<CharSequence> text = new ArrayList<String>(5);
	text.add("Hello");
	String test = text.get(0);
}
ولكن هذا غير ممكن وسوف نحصل على الخطأ: error: incompatible types: ArrayList<String> cannot be converted to ArrayList<CharSequence> يبدو غريبا، لأنه. السطر CharSequence sec = "test";لا يحتوي على أخطاء. دعونا معرفة ذلك. ويقولون عن هذا السلوك: "الأدوية الجنيسة ثابتة". ما هو "الثابت"؟ يعجبني ما قيل عن هذا في ويكيبيديا في مقالة " التباين والتباين ":
الأدوية العامة للقطط - 4
وبالتالي، فإن الثبات هو غياب الميراث بين الأنواع المشتقة. إذا كانت Cat نوعًا فرعيًا من الحيوانات، فإن Set<Cats> ليس نوعًا فرعيًا من Set<Animals> وSet<Animals> ليس نوعًا فرعيًا من Set<Cats>. بالمناسبة، تجدر الإشارة إلى أنه بدءًا من Java SE 7، ظهر ما يسمى بـ " Diamond Operator ". لأن قوسي الزاوية <> يشبهان الماس. هذا يسمح لنا باستخدام الأدوية العامة مثل هذا:
public static void main(String[] args) {
  List<String> lines = new ArrayList<>();
  lines.add("Hello world!");
  System.out.println(lines);
}
بناءً على هذا الكود، يفهم المترجم أنه إذا أشرنا على الجانب الأيسر إلى أنه Listسيحتوي على كائنات من النوع String، فإننا على الجانب الأيمن نعني أننا نريد حفظ linesقائمة ArrayList جديدة في متغير، والذي سيخزن أيضًا كائنًا من النوع المحدد على الجانب الأيسر. لذا فإن المترجم من الجانب الأيسر يفهم أو يستنتج النوع الموجود على الجانب الأيمن. ولهذا السبب يُسمى هذا السلوك باستدلال النوع أو "استدلال النوع" باللغة الإنجليزية. شيء آخر مثير للاهتمام جدير بالملاحظة هو أنواع RAW أو "الأنواع الخام". لأن لم تكن الأدوية العامة موجودة دائمًا، وتحاول Java الحفاظ على التوافق مع الإصدارات السابقة كلما أمكن ذلك، ثم تضطر الأدوية العامة إلى العمل بطريقة ما مع التعليمات البرمجية التي لم يتم تحديدها بشكل عام. دعونا نرى مثالا:
List<CharSequence> lines = new ArrayList<String>();
كما نتذكر، لن يتم تجميع مثل هذا الخط بسبب ثبات الأدوية العامة.
List<Object> lines = new ArrayList<String>();
ولن يتم تجميع هذا أيضًا لنفس السبب.
List lines = new ArrayList<String>();
List<String> lines2 = new ArrayList();
سيتم تجميع هذه الخطوط وستعمل. يتم استخدام الأنواع الخام فيها، أي. أنواع غير محددة. مرة أخرى، تجدر الإشارة إلى أنه لا ينبغي استخدام الأنواع الأولية في التعليمات البرمجية الحديثة.
الأدوية العامة للقطط - 5

الطبقات المكتوبة

لذلك، كتبت الطبقات. دعونا نرى كيف يمكننا كتابة فصلنا المكتوب. على سبيل المثال، لدينا التسلسل الهرمي للفئة:
public static abstract class Animal {
  public abstract void voice();
}

public static class Cat extends Animal {
  public void voice(){
    System.out.println("Meow meow");
  }
}

public static class Dog extends Animal {
  public void voice(){
    System.out.println("Woof woof");
  }
}
نريد إنشاء فصل ينفذ حاوية حيوانية. سيكون من الممكن كتابة فصل يحتوي على أي Animal. هذا أمر بسيط ومفهوم، ولكن... الاختلاط بين الكلاب والقطط أمر سيء، فهم ليسوا أصدقاء مع بعضهم البعض. بالإضافة إلى ذلك، إذا حصل شخص ما على مثل هذه الحاوية، فقد يرمي القطط عن طريق الخطأ من الحاوية إلى مجموعة من الكلاب... وهذا لن يؤدي إلى أي خير. وهنا سوف تساعدنا الأدوية الجنيسة. على سبيل المثال، دعونا نكتب التنفيذ مثل هذا:
public static class Box<T> {
  List<T> slots = new ArrayList<>();
  public List<T> getSlots() {
    return slots;
  }
}
سيعمل فصلنا مع كائنات من النوع المحدد بواسطة اسم عام يسمى T. وهذا نوع من الاسم المستعار. لأن يتم تحديد العام في اسم الفئة، ثم سنتلقاه عند الإعلان عن الفئة:
public static void main(String[] args) {
  Box<Cat> catBox = new Box<>();
  Cat murzik = new Cat();
  catBox.getSlots().add(murzik);
}
كما نرى، أشرنا إلى أن لدينا Box، والذي يعمل فقط مع Cat. أدرك المترجم أنه catBoxبدلاً من النوع العام، Tتحتاج إلى استبدال النوع Catأينما تم تحديد اسم النوع العام T:
الأدوية العامة للقطط - 6
أولئك. إنه بفضل Box<Cat>المترجم أنه يفهم ما slotsينبغي أن يكون في الواقع List<Cat>. لأنه Box<Dog>في الداخل سيكون هناك slots, يحتوي على List<Dog>. من الممكن أن يكون هناك العديد من الأدوية العامة في إعلان النوع، على سبيل المثال:
public static class Box<T, V> {
يمكن أن يكون الاسم العام أي شيء، على الرغم من أنه يوصى بالالتزام ببعض القواعد غير المعلنة - "اصطلاحات تسمية معلمات النوع": نوع العنصر - E، نوع المفتاح - K، نوع الرقم - N، T - للنوع، V - لـ نوع القيمة. بالمناسبة، تذكر أننا قلنا أن الأدوية العامة ثابتة، أي. لا تحافظ على التسلسل الهرمي للميراث. في الواقع، يمكننا التأثير على هذا. وهذا هو، لدينا الفرصة لجعل الأدوية العامة COvariant، أي. الحفاظ على الميراث بنفس الترتيب. يسمى هذا السلوك "النوع المحدود"، أي. أنواع محدودة. على سبيل المثال، يمكن أن يحتوي فصلنا Boxعلى جميع الحيوانات، ثم نعلن عن فئة عامة مثل هذا:
public static class Box<T extends Animal> {
أي أننا وضعنا الحد الأعلى للفئة Animal. يمكننا أيضًا تحديد عدة أنواع بعد الكلمة الأساسية extends. وهذا يعني أن النوع الذي سنعمل معه يجب أن يكون سليلًا لفئة معينة وفي نفس الوقت يقوم بتنفيذ واجهة ما. على سبيل المثال:
public static class Box<T extends Animal & Comparable> {
في هذه الحالة، إذا حاولنا وضع Boxشيء ما في مكان ليس وريثًا Animalولا يتم تنفيذه Comparable، فسنتلقى خطأ أثناء التجميع:
error: type argument Cat is not within bounds of type-variable T
الأدوية العامة للقطط - 7

طريقة الكتابة

يتم استخدام الأدوية العامة ليس فقط في الأنواع، ولكن أيضًا في الطرق الفردية. يمكن رؤية تطبيق الطرق في البرنامج التعليمي الرسمي: " الطرق العامة ".

خلفية:

الأدوية العامة للقطط - 8
دعونا ننظر إلى هذه الصورة. كما ترون، ينظر المترجم إلى توقيع الطريقة ويرى أننا نأخذ فئة غير محددة كمدخل. لا يحدد بالتوقيع أننا نعيد شيئًا ما، أي. هدف. لذلك، إذا أردنا إنشاء ArrayList، على سبيل المثال، فعلينا القيام بذلك:
ArrayList<String> object = (ArrayList<String>) createObject(ArrayList.class);
عليك أن تكتب صراحة أن الإخراج سيكون ArrayList، وهو أمر قبيح ويضيف فرصة لارتكاب خطأ. على سبيل المثال، يمكننا أن نكتب مثل هذا الهراء وسوف يجمع:
ArrayList object = (ArrayList) createObject(LinkedList.class);
هل يمكننا مساعدة المترجم؟ نعم، الأدوية الجنيسة تسمح لنا بالقيام بذلك. لننظر إلى نفس المثال:
الأدوية العامة للقطط - 9
بعد ذلك، يمكننا إنشاء كائن ببساطة مثل هذا:
ArrayList<String> object = createObject(ArrayList.class);
الأدوية العامة للقطط - 10

WildCard

وفقًا لبرنامج Oracle التعليمي حول الأدوية العامة، وتحديدًا قسم " Wildcards "، يمكننا وصف "نوع غير معروف" بعلامة استفهام. Wildcard هي أداة مفيدة للتخفيف من بعض القيود المفروضة على الأدوية الجنيسة. على سبيل المثال، كما ناقشنا سابقًا، الأدوية العامة ثابتة. هذا يعني أنه على الرغم من أن جميع الفئات هي سلالات (أنواع فرعية) من نوع الكائن، إلا أنها List<любой тип>ليست نوعًا فرعيًا List<Object>. ولكن، List<любой тип>إنه نوع فرعي List<?>. لذا يمكننا كتابة الكود التالي:
public static void printList(List<?> list) {
  for (Object elem: list) {
    System.out.print(elem + " ");
  }
  System.out.println();
}
مثل الأدوية العامة العادية (أي بدون استخدام أحرف البدل)، يمكن أن تكون الأدوية العامة التي تحتوي على أحرف البدل محدودة. يبدو حرف البدل ذو الحدود العليا مألوفًا:
public static void printCatList(List<? extends Cat> list) {
  for (Cat cat: list) {
    System.out.print(cat + " ");
  }
  System.out.println();
}
ولكن يمكنك أيضًا تحديده باستخدام حرف البدل ذي الحد الأدنى:
public static void printCatList(List<? super Cat> list) {
وبالتالي، ستبدأ الطريقة في قبول جميع القطط، وكذلك أعلى في التسلسل الهرمي (حتى الكائن).
الأدوية العامة للقطط - 11

اكتب المحو

عند الحديث عن الأدوية العامة، من المفيد معرفة "محو الكتابة". في الواقع، محو الكتابة يتعلق بحقيقة أن الأدوية العامة هي معلومات للمترجم. أثناء تنفيذ البرنامج، لا توجد معلومات إضافية حول الأدوية العامة، وهذا ما يسمى "المحو". يؤدي هذا المحو إلى استبدال النوع العام بالنوع المحدد. إذا لم يكن للنوع العام حدود، فسيتم استبدال نوع الكائن. إذا تم تحديد الحدود (على سبيل المثال <T extends Comparable>)، فسيتم استبدالها. فيما يلي مثال من برنامج Oracle التعليمي: " محو الأنواع العامة ":
الأدوية العامة للقطط - 12
وكما تقدم، ففي هذا المثال Tتم مسح الجنس إلى حده، أي: إلى حده. قبل Comparable.
الأدوية العامة للقطط - 13

خاتمة

الأدوية العامة هي موضوع مثير للاهتمام للغاية. أتمنى أن يكون هذا الموضوع محل اهتمامكم. لتلخيص ذلك، يمكننا القول أن الأدوية العامة هي أداة ممتازة تلقاها المطورون لمطالبة المترجم بمعلومات إضافية لضمان سلامة الكتابة من ناحية والمرونة من ناحية أخرى. وإذا كنت مهتمًا، فأقترح عليك مراجعة المصادر التي أعجبتني: # فياتشيسلاف
تعليقات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION