1.
أولاً ، دعونا نلقي نظرة على الرمز القصير التالي:
الفئة العامة generictest {public static void main (string [] args) {list list = new ArrayList () ؛ list.add ("qqyumidi") ؛ list.add ("الذرة") ؛ list.add (100) ؛ لـ (int i = 0 ؛ i <list.size () ؛ i ++) {string name = (string) list.get (i) ؛ // 1 system.out.println ("name:" + name) ؛ }}}حدد مجموعة من نوع القائمة ، أولاً إضافة قيمتين من نوع السلسلة ، ثم إضافة قيمة نوع عدد صحيح. هذا مسموح به تمامًا لأن النوع الافتراضي للقائمة هو كائن في هذا الوقت. في الحلقة اللاحقة ، من السهل أن يكون لديك أخطاء مماثلة لـ // 1 لأنني نسيت إضافة قيم نوع عدد صحيح أو أسباب تشفير أخرى في القائمة من قبل. نظرًا لأن مرحلة التجميع طبيعية ، سيظهر استثناء "java.lang.classcastexception" في وقت التشغيل. لذلك ، يصعب اكتشاف هذه الأخطاء أثناء عملية الترميز.
أثناء عملية الترميز على النحو الوارد أعلاه ، وجدنا أن هناك مشكلتان رئيسيتان:
1. عندما نضع كائن في المجموعة ، لن تتذكر المجموعة نوع هذا الكائن. عندما يتم إخراج هذا الكائن من المجموعة مرة أخرى ، يصبح نوع الترجمة للكائن الذي تم تغييره هو نوع الكائن ، لكن نوع وقت التشغيل الخاص به لا يزال نوعه الخاص.
2. لذلك ، عند إخراج عنصر التجميع في // 1 ، يجب تحويل الأنواع القسرية بشكل مصطنع إلى نوع الهدف المحدد ، ومن السهل رؤية استثناء "java.lang.classcastexception".
فهل هناك أي طريقة لجعل المجموعة تذكر أنواعًا مختلفة من العناصر في المجموعة ، ولتحقيق ذلك طالما لم تكن هناك مشكلة أثناء التجميع ، فلن يكون هناك "java.lang.classcastexception" خلال وقت التشغيل؟ الجواب هو استخدام الأدوية الجيلية.
2. ما هو عام؟
الأدوية ، وهذا هو ، "نوع المعلمة". عندما يتعلق الأمر بالمعلمات ، فإن الشيء الأكثر دراية هو وجود معلمات ملموسة عند تحديد طريقة ما ، ثم تمرير المعلمات الفعلية عند استدعاء هذه الطريقة. فكيف تفهم نوع المعلمة؟ كما يوحي الاسم ، فهذا يعني معلمة النوع من النوع المحدد الأصلي ، على غرار المعلمات المتغيرة في الطريقة. في هذا الوقت ، يتم تعريف النوع أيضًا على أنه نموذج معلمة (يمكن أن يطلق عليه معلمة رسمية من النوع) ، ثم يتم تمرير النوع المحدد (المعلمة الفعلية) عند استخدامه/استدعاءه.
يبدو معقدة بعض الشيء. أولاً ، دعنا نلقي نظرة على المثال أعلاه باستخدام الكتابة العامة.
الفئة العامة generictest {public static void main (string [] args) { /* list = new ArrayList () ؛ list.add ("qqyumidi") ؛ list.add ("الذرة") ؛ list.add (100) ؛ */ list <String> list = new ArrayList <Tring> () ؛ list.add ("qqyumidi") ؛ list.add ("الذرة") ؛ //list.add(100) ؛ // 1 يطالب خطأ التجميع لـ (int i = 0 ؛ i <list.size () ؛ i ++) {string name = list.get (i) ؛ // 2 system.out.println ("name:" + name) ؛ }}}بعد استخدام الكتابة العامة ، يحدث خطأ في التجميع عندما تريد إضافة كائن نوع عدد صحيح على // 1. من خلال القائمة <String> ، من المحدود مباشرة أنه يمكن احتواء عناصر نوع السلسلة فقط في مجموعة القائمة ، لذلك ليست هناك حاجة لإلقاء النوع على // 2 ، لأنه في هذا الوقت ، يمكن للمجموعة أن تتذكر معلومات النوع للعنصر ، ويمكن للمترجم أن يؤكد أنه نوع السلسلة.
دمج التعريف العام أعلاه ، نعلم أنه في القائمة <string> ، السلسلة هي معلمة نوع ، أي ، يجب أن تحتوي واجهة القائمة المقابلة على معلمات رسمية من النوع. علاوة على ذلك ، فإن نتيجة الإرجاع لطريقة GET () هي مباشرة هذا النوع الرسمي المعلمة (أي ، المعلمة النوع الواردة المقابلة). دعنا نلقي نظرة على التعريف المحدد لواجهة القائمة:
قائمة الواجهة العامة <e> تمتد المجموعة <e> {int size () ؛ منطقية isempty () ؛ Boolean يحتوي على (كائن O) ؛ iterator <e> iterator () ؛ كائن [] tararray () ؛ <t> t [] tararray (t [] a) ؛ إضافة منطقية (e e) ؛ إزالة منطقية (كائن O) ؛ يحتوي Boolean ConvaleAll (Collection <؟> C) ؛ addall منطقية (مجموعة <؟ تمديد e> c) ؛ Boolean Addall (Int Index ، Collection <؟ Extends e> c) ؛ removeall boolean (مجموعة <؟> c) ؛ Boolean ReseaNall (Collection <؟> C) ؛ باطل clear () ؛ منطقية يساوي (كائن O) ؛ int hashcode () ؛ e get (int index) ؛ مجموعة E (int index ، e element) ؛ إضافة باطلة (int index ، e element) ؛ e إزالة (int index) ؛ int indexof (كائن O) ؛ int lastIndExof (كائن O) ؛ listIritiTorator <e> listIritiTorator () ؛ ListIritorator <E> listIratorator (int index) ؛ قائمة <e> المحزن (int fromIndex ، int toindex) ؛}يمكننا أن نرى أنه بعد اعتماد التعريف العام في واجهة القائمة ، تمثل E في <e> معلمة رسمية من النوع ، والتي يمكن أن تتلقى معلمات نوع محددة. في تعريف الواجهة هذا ، حيث يظهر E ، فهذا يعني أن نفس المعلمات المقبولة من الخارج مقبولة.
بطبيعة الحال ، ArrayList هو فئة تنفيذ لواجهة القائمة ، ونموذج التعريف الخاص به هو:
يمتد ArrayList Public Class <E> قائمة مجردة <e> قائمة على القائمة <e> ، عشوائي ، clonable ، java.io.serializable {public boolean add (e e) {insureCapacityInternal (size + 1) ؛ // زيادة modcount !! elementData [size ++] = e ؛ العودة صحيح. } public e get (int index) {rangecheck (index) ؛ checkForComodification () ؛ return ArrayList.This.ElementData (إزاحة + فهرس) ؛ } //...omit عمليات تعريف محددة أخرى}من هذا ، نفهم من منظور رمز المصدر سبب تجميع كائن نوع عدد صحيح بشكل غير صحيح في // 1 ، والنوع الذي تم الحصول عليه في // 2 هو نوع السلسلة مباشرة.
3. تخصيص واجهات عامة وفئات عامة وطرق عامة
من المحتوى أعلاه ، فهم الجميع عملية التشغيل المحددة للأعداء. ومن المعروف أيضًا أنه يمكن أيضًا تعريف الواجهات والفئات والأساليب باستخدام الأدوية الجينية واستخدامها وفقًا لذلك. نعم ، عند استخدامها على وجه التحديد ، يمكن تقسيمها إلى واجهات عامة ، فئات عامة وطرق عامة.
تشبه الواجهات العامة المخصصة والفئات العامة والطرق العامة القائمة وقائمة ArrayList في رمز مصدر Java أعلاه. على النحو التالي ، ننظر إلى أبسط تعريف الطبقة العامة والطريقة:
الفئة العامة generictest {public static void main (string [] args) {box <string> name = new box <string> ("corn") ؛ System.out.println ("name:" + name.getData ()) ؛ }} مربع الفئة <T> {private t data ؛ المربع العام () {} المربع العام (t data) {this.data = data ؛ } public t getData () {return data ؛ }}في عملية تحديد الواجهات العامة ، والفئات العامة والطرق العامة ، غالبًا ما يتم استخدام المعلمات الشائعة مثل T و E و K و V ، وما إلى ذلك لتمثيل المعلمات الرسمية العامة لأنها تتلقى معلمات النوع التي تم تمريرها من الاستخدام الخارجي. لذلك بالنسبة لأنواع مختلفة من المعلمات الواردة ، هل تم إنشاء أنواع مثيلات الكائن المقابلة نفس الشيء؟
الفئة العامة generictest {public static void main (string [] args) {box <string> name = new box <string> ("corn") ؛ Box <integer> Age = New Box <integer> (712) ؛ System.out.println ("class name:" + name.getClass ()) ؛ // com.qyumidi.box system.out.println ("Age Class:" + Age.getClass ()) ؛ // com.qyumidi.box system.out.println (name.getClass () == Age.getClass ()) ؛ // حقيقي }}من هذا ، وجدنا أنه عند استخدام فئات عامة ، على الرغم من أن الحجج العامة المختلفة يتم تمريرها ، لا يتم إنشاء أنواع مختلفة بالمعنى الحقيقي. لا يوجد سوى فئة عامة واحدة تمر في وسيطات عامة مختلفة في الذاكرة ، أي أنها لا تزال هي النوع الأساسي الأصلي (المربع في هذا المثال). بالطبع ، من الناحية المنطقية ، يمكننا فهمها على أنها أنواع عامة مختلفة متعددة.
والسبب هو أن الغرض من مفهوم الأدوية في جافا هو أنه يعمل فقط في مرحلة تجميع الكود. أثناء عملية التجميع ، بعد التحقق من النتائج العامة بشكل صحيح ، سيتم محو المعلومات ذات الصلة للأولاد. وهذا يعني أن ملف الفئة الذي تم تجميعه بنجاح لا يحتوي على أي معلومات عامة. لن تدخل المعلومات العامة في مرحلة وقت التشغيل.
تم تلخيص ذلك في جملة واحدة: تعتبر الأنواع العامة منطقيًا أنواعًا مختلفة متعددة ، وهي في الواقع نفس الأنواع الأساسية.
أربعة. اكتب Wildcard
باتباع الاستنتاج أعلاه ، نعلم أن المربع <number> و box <integer> هما في الواقع كلا النوعين من المربعين. الآن نحن بحاجة إلى الاستمرار في استكشاف سؤال. لذلك ، من الناحية المنطقية ، يمكن اعتبار المربع <number> و box <integer> أنواعًا عامة مع علاقات الوالدين والطفل؟
لتوضيح هذه المشكلة ، دعنا نستمر في النظر إلى المثال التالي:
الفئة العامة generictest {public static void main (string [] args) {box <mumber> name = new box <mumber> (99) ؛ Box <integer> Age = New Box <integer> (712) ؛ getData (الاسم) ؛ . // 1} public static void getData (box <number> data) {system.out.println ("data:" + data.getData ()) ؛ }}لقد وجدنا أن رسالة خطأ ظهرت في Code // 1: طريقة getData (المربع <morm>) في T ype enderictest لا تنطبق على الوسيطات (المربع <integer>). من الواضح ، من خلال المطالبة بالمعلومات ، نعلم أن المربع <morm> لا يمكن اعتباره منطقياً على أنه فئة الأصل من المربع <integer>. إذن ، ما هو السبب؟
الفئة العامة generictest {public static void main (string [] args) {box <integer> a = new box <integer> (712) ؛ المربع <bumber> b = a ؛ // 1 Box <Loat> f = new box <loat> (3.14f) ؛ B.SetData (F) ؛ // 2} public static void getData (box <number> data) {system.out.println ("data:" + data.getData ()) ؛ }} مربع الفئة <T> {private t data ؛ المربع العام () {} المربع العام (t data) {setData (data) ؛ } public t getData () {return data ؛ } public void setData (t data) {this.data = data ؛ }}في هذا المثال ، ستكون هناك رسالة خطأ في // 1 و // 2. هنا يمكننا استخدام طريقة مضادة لشرح ذلك.
على افتراض أن المربع <morm> يمكن اعتباره منطقيًا على أنه فئة الأصل من المربع <integer> ، فلن يكون هناك مطالبات خطأ في // 1 و // 2. ثم تنشأ المشكلة. ما النوع الذي هو عند جلب البيانات من خلال طريقة getData ()؟ عدد صحيح؟ يطفو؟ أو الرقم؟ علاوة على ذلك ، نظرًا للترتيب الذي لا يمكن السيطرة عليه في عملية البرمجة ، يجب إصدار الحكم عند الضرورة ويتم إجراء تحويل النوع. من الواضح أن هذا يتناقض مع فكرة الأدوية الجيرية ، لذلك من المنطقي ، لا يمكن اعتبار المربع <morm> فئة الأصل من المربع <integer>.
حسنًا ، ثم دعونا نلقي نظرة على المثال الأول في "Type Wildcards" ، نحن نعرف السبب الأعمق لمطالبات الخطأ المحددة الخاصة به. فكيف تحلها؟ يمكن للمقر يمكن تحديد وظيفة جديدة. من الواضح أن هذا يتعارض مع مفهوم الأشكال في Java ، لذلك نحتاج إلى نوع مرجعي يمكن استخدامه منطقيًا لتمثيل الفئة الأصل لكلا المربعين <integer> و Box <mord> ، وبالتالي ، ظهرت نوع Bildcard.
يتم استخدام البطاقات البرية اكتب عمومًا بدلاً من وسيطات النوع المحددة. لاحظ أن هذه معلمة نوع ، وليس معلمة نوع! و Box <؟> هل من المنطقي الفئة الأصل لجميع المربع <integer> ، المربع <morm> ... ، إلخ. لذلك ، لا يزال بإمكاننا تحديد الطرق العامة لتحقيق هذه المتطلبات.
الفئة العامة generictest {public static void main (string [] args) {box <string> name = new box <string> ("corn") ؛ Box <integer> Age = New Box <integer> (712) ؛ المربع <morm> رقم = مربع جديد <mumber> (314) ؛ getData (الاسم) ؛ getData (العمر) ؛ getData (رقم) ؛ } public static void getData (box <؟> data) {system.out.println ("data:" + data.getData ()) ؛ }}في بعض الأحيان ، قد نسمع أيضًا عن الأنواع العلوية والسفلية من البطاقات البرية. كيف هو بالضبط؟
في المثال أعلاه ، إذا كنت بحاجة إلى تحديد طريقة تعمل بشكل مشابه لـ GetData () ، ولكن هناك مزيد من القيود على وسيطات النوع: يمكن أن تكون فئة الأرقام والفئات الفرعية فقط. في هذا الوقت ، مطلوب الحد الأعلى من الكلام البري.
الفئة العامة generictest {public static void main (string [] args) {box <string> name = new box <string> ("corn") ؛ Box <integer> Age = New Box <integer> (712) ؛ المربع <morm> رقم = مربع جديد <mumber> (314) ؛ getData (الاسم) ؛ getData (العمر) ؛ getData (رقم) ؛ // getuppernumberdata (name) ؛ // 1 getuppernumberdata (العمر) ؛ // 2 getuppernumberdata (رقم) ؛ // 3} public static void getData (box <؟> data) {system.out.println ("data:" + data.getData ()) ؛ ) }}في هذه المرحلة ، من الواضح أن المكالمة في Code // 1 ستظهر رسالة خطأ ، في حين أن المكالمة AT // 2 // 3 ستكون طبيعية.
يتم تعريف الحد الأعلى من الأشعة البرية من النوع بواسطة شكل المربع <؟ يمتد الرقم>. في المقابل ، فإن الحد الأدنى من الأشعة البرية النوع هو شكل مربع <؟ الرقم السوبر> ، ومعنىه هو بالضبط عكس الحد الأعلى من الكلام البري من النوع. لن أشرح ذلك كثيرًا هنا.
5. الفصل الإضافي
يتم الاستشهاد بالأمثلة في هذه المقالة بشكل أساسي لتوضيح بعض الأفكار في الأدوية وليس لها بالضرورة قابلية استخدام عملية. بالإضافة إلى ذلك ، عندما يتعلق الأمر بالأمراء ، أعتقد أن أكثر ما تستخدمه هو في المجموعة. في الواقع ، في عملية البرمجة الفعلية ، يمكنك استخدام الأداء العام لتبسيط التطوير ويمكن أن تضمن جودة الكود جيدًا. والشيء الوحيد الذي يجب ملاحظة أنه لا يوجد ما يسمى صفيف عام في جافا.
بالنسبة للذهل الأولي ، فإن الشيء الأكثر أهمية هو فهم الأفكار والأغراض التي تقف وراءها.
ما ورد أعلاه هو المحتوى الكامل لسلسلة ملخص Java: Java Generics التفسيرات المفصلة التي قدمها لك المحرر. آمل أن يكون ذلك مفيدًا للجميع ودعم wulin.com أكثر ~