1. ما هو بالضبط عام؟
قبل مناقشة الاستنتاج النوع ، يجب علينا مراجعة ما هو عام. الأدوية الجيرية هي ميزات جديدة من Java SE 1.5. جوهر الأدوية هو نوع محدد ، أي نوع البيانات الذي يتم تشغيله كمعلمة. من حيث العلمان ، سيكون "نوع متغيرات". يمكن استخدام هذا النوع من المتغير في إنشاء الفئات والواجهات والأساليب. أسهل طريقة لفهم جافا جيرلسيك هي اعتبارها بناء جملة مريحة يمكن أن توفر لك بعض العمليات على casting نوع جافا:
قائمة <Apple> box = new ArrayList <Apple> () ؛ box.add (New Apple ()) ؛ Apple Apple = box.get (0) ؛
تم التعبير عن الكود أعلاه نفسه بوضوح: المربع عبارة عن List بها كائنات Apple. تقوم طريقة get بإرجاع مثيل كائن Apple ، ولا تتطلب هذه العملية تحويل النوع. لا يوجد أي عام ، يجب كتابة الرمز أعلاه على هذا النحو:
Apple Apple = (Apple) box.get (0) ؛
بطبيعة الحال ، فإن الأدوية الجيلية ليست بسيطة مثل ما وصفته هنا ، لكن هذا ليس بطل الرواية في عصرنا. يحتاج الطلاب الذين لا يفهمون بشكل جيد للغاية إلى تعويض الدروس ~ بالطبع ، لا تزال أفضل المواد المرجعية هي المستندات الرسمية.
2. المشاكل التي تسببها الأدوية (قبل Java 7)
أكبر ميزة للذهل الأولي هي أنها توفر نوع سلامة البرنامج ويمكن أن تكون متوافقة مع الوراء. ومع ذلك ، هناك أيضًا أشياء تجعل المطورين غير راضين. يجب كتابة نوع الأدوية في كل مرة يحددها. لا تشعر مواصفات العرض هذه ببعض الفصول قليلاً فحسب ، ولكن الأهم من ذلك ، أن العديد من المبرمجين ليسوا على دراية بالأجور الأولي ، لذلك لا يمكنهم إعطاء معلمات النوع الصحيحة في كثير من الحالات. الآن ، يقوم برنامج التحويل البرمجي تلقائيًا بإصدار أنواع المعلمات من الأدوية ، والتي يمكن أن تقلل من هذا الموقف وتحسين قابلية قراءة التعليمات البرمجية.
3. التحسينات في اشتقاق نوع الأدوية في جافا 7
يتطلب استخدام أنواع عامة في الإصدارات السابقة من Java 7 إضافة أنواع عامة على كلا الجانبين عند إعلان القيم وتعيينها. على سبيل المثال:
الخريطة <string ، integer> map = new hashmap <string ، integer> () ؛
يجب أن يكون الكثير من الناس هو نفسه مثلي في البداية ، وكانوا في حيرة من هذا: ألم أعلنت نوع المعلمة في الإعلان المتغير؟ لماذا ما زلت بحاجة إلى كتابتها عند تهيئة الكائن؟ هذا هو ما يجعل الأدوية الجنيسة تشكو منه عندما ظهرت لأول مرة. ومع ذلك ، فمن دواعي سرور أنه على الرغم من أن Java تتحسن ، فإن المصممين يقومون أيضًا بتحسين برنامج التحويل البرمجي Java باستمرار لجعله أكثر ذكاءً وإنسنا. إليكم بطل الرواية اليوم: اكتب pushdown ... حسنًا ... إنه ليس اشتقاقًا ، أي نوع الاستدلال. عندما يظهر هذا الرجل ، عندما يكتب الرمز أعلاه ، يمكنه حذف أنواع المعلمات بسعادة عند إنشاء مثيل لثبات الكائن ، ويصبح هكذا:
الخريطة <string ، integer> map = new hashmap <> () ؛
في هذا البيان ، سيستنتج المترجم تلقائيًا النوع العام عند إنشاء HashMap على أساس النوع العام عند إعلان متغير. مرة أخرى ، يرجى التأكد من الانتباه إلى "<>" وراء new HashMap . فقط عن طريق إضافة هذا "<>" يعني أنه استدلال تلقائي ، وإلا فهو HashMap غير عام ، وسيتم إعطاء موجه تحذير عند تجميع الكود المصدري باستخدام المترجم. يسمى هذا الزوج من أقواس الزاوية "<>" "الماس" في المستند الرسمي.
ومع ذلك ، فإن اشتقاق النوع في هذا الوقت غير مكتمل (حتى منتج شبه مصقول) ، لأن الاستدلال نوعًا عند إنشاء مثيلات عامة في Java SE 7 محدود: فقط إذا تم الإعلان عن النوع المعلمة من المنشئ بشكل كبير في السياق ، يمكن استخدام التداول النوع ، وإلا فلن يعمل. على سبيل المثال: لا يمكن تجميع المثال التالي بشكل صحيح في Java 7 (ولكن يمكن تجميعه في Java 8 الآن ، لأنه يتم استنتاج النوع العام تلقائيًا بناءً على معلمات الطريقة):
قائمة <Tring> list = new ArrayList <> () ؛ list.add ("A") ؛ // لأن Addall تتوقع الحصول على معلمات من مجموعة النوع <؟ يمتد String> ، لا يمكن للبيان التالي تمرير List.addall (ArrayList <> () جديد) ؛4. Reevolution في Java 8
في أحدث وثائق Java الرسمية ، يمكننا أن نرى تعريف اشتقاق النوع:
إن الاستدلال نوع هو قدرة مترجم Java على النظر إلى كل طريقة الاحتجاج والإعلان المقابل لتحديد وسيطة النوع (أو الوسيطات) التي تجعل الاحتجاج قابلة للتطبيق. تحدد خوارزمية الاستدلال أنواع الوسائط ، وإذا كانت متوفرة ، فإن النوع الذي يتم تعيين النتيجة أو إرجاعه. أخيرًا ، تحاول خوارزمية الاستدلال العثور على النوع الأكثر تحديداً الذي يعمل مع جميع الحجج.
باختصار ، يشير اشتقاق النوع إلى قدرة التحويل البرمجي على تحديد أنواع المعلمات المطلوبة بناءً على الطريقة التي تتصل بها والإعلان المقابل. ويتم تقديم مثال أيضًا في الوثائق الرسمية لشرح:
ثابت <t> t اختيار (t a1 ، t a2) {return a2 ؛ } serializable s = pick ("d" ، ArrayList New ArrayList <string> ()) ؛ هنا ، يمكن للمترجم أن يستنتج أن نوع المعلمة الثانية التي تم تمريرها في طريقة pick Serializable .
في إصدارات Java السابقة ، إذا كان المثال أعلاه قادرًا على التجميع ، فأنت بحاجة إلى كتابة هذا:
serializable s = this. <serializable> pick ("d" ، new ArrayList <string> ()) ؛يمكن رؤية السبب التفصيلي لكتابة هذا في الفصل العام لفكر برمجة Bruce Eckel Java (الطبعة الرابعة). بالطبع ، يعتمد هذا الكتاب على Java 6 ، وهذا الإصدار ليس له مفهوم للاشتقار نوع. رؤية هذا ، يمكن أن يرى الكثير من الناس بوضوح قوة اشتقاق النوع في أحدث إصدار. لم يعد يقتصر على عملية الإعلان والتثبيت في الطبقات العامة ، ولكن يمتد إلى طرق ذات معلمات عامة.
4.1 نوع الاستدلال والطرق العامة
فيما يتعلق بنوع الاشتقاق والطرق العامة في الإصدار الجديد ، يقدم المستند أيضًا مثالًا أكثر تعقيدًا قليلاً. لقد نشرته هنا. المبدأ هو نفس المثال Serializable أعلاه ، لذلك لن أخوض في التفاصيل. إذا كنت ترغب في توحيدها ، يمكنك إلقاء نظرة:
public class boxdemo {public static <u> void addbox (u u ، java.util.list <box <u>> boxes) {box <u> box = new box <> () ؛ box.set (u) ؛ boxes.add (box) ؛ } static public <u> void outputboxes (java.util.list <box <u>> boxes) {int counter = 0 ؛ لـ (box <u> box: boxes) {u boxContents = box.get () ؛ System.out.println ("Box #" + Counter + "يحتوي على [" + boxcontents.toString () + "]") ؛ Counter ++ ؛ }} public static void main (string [] args) {java.util.arraylist <box <integer >> listofintegerboxes = new java.util.arraylist <> () ؛ boxdemo. <integer> addbox (integer.valueof (10) ، listofintegerboxes) ؛ boxdemo.addbox (integer.valueof (20) ، listofintegerboxes) ؛ boxdemo.addbox (integer.valueof (30) ، listofintegerboxes) ؛ boxdemo.outputboxes (listofintegerboxes) ؛ }}إخراج الرمز أعلاه هو:
يحتوي المربع #0 على [10] المربع رقم 1 [20] يحتوي المربع رقم 2 على [30]
اسمحوا لي أن أذكر أن تركيز الطريقة العامة addBox هو وصف النوع الذي لم تعد بحاجة إلى عرضه في استدعاء الطريقة في إصدار Java الجديد ، مثل هذا:
boxdemo. <integer> addbox (integer.valueof (10) ، listofintegerboxes) ؛
يمكن للمترجم تلقائيًا استنتاج أن نوع المعلمة Integer من المعلمات التي تم تمريرها في addBox .
4.2 نوع الاستدلال والمنشآت العامة للفصول العامة وغير الجينية
حسنًا ... قد تكون هذه جملة أفضل في اللغة الإنجليزية: نوع الاستدلال والمنشآت العامة للدروس العامة وغير الجينية
في الواقع ، المنشئون العامون ليسوا براءات اختراع للفصول العامة. يمكن أن يكون لدى الطبقات غير العامة أيضًا منشئاتها العامة. ألق نظرة على هذا المثال:
الفئة myClass <x> {<t> myclass (t t) {// ...}}إذا تم إنشاء مثيل التالي لفئة MyClass:
myClass جديد <integer> ("") حسنًا ، نوضح هنا أن نوع المعلمة X من myClass هو Integer ، وبالنسبة للمُشئ ، يستنتج المترجم أن المعلمة الرسمية t هي String استنادًا إلى كائن String الواردة (""). تم تنفيذ هذا في إصدار Java7. ما هي التحسينات التي تم إجراؤها في Java8؟ بعد Java8 ، يمكننا كتابة هذا الاستئصال لفئة عامة مع مُنشئ عام مثل هذا:
myClass <integer> myobject = new myClass <> ("") ؛ نعم ، لا يزال زوج الأقواس الزاوية (<>) ، والتي تسمى Diamond ، بحيث يمكن للمترجم لدينا تلقائيًا أن يستنتج أن المعلمات الرسمية X هي Integer و T String . يشبه هذا في الواقع مثالنا الأولي على Map<String,String> ، باستثناء أن هناك اعتلاقة من المُنشئ.
تجدر الإشارة إلى أن اشتقاق النوع لا يمكن اشتقاقه إلا بناءً على نوع معلمة المكالمة ، ونوع الهدف (سيتم مناقشته قريبًا) ونوع الإرجاع (إذا كان هناك عائد) ، ولا يمكن اشتقاقه بناءً على بعض المتطلبات بعد البرنامج.
4.3 نوع الهدف
كما ذكرنا سابقًا ، يمكن للمترجم إجراء اشتقاق من النوع استنادًا إلى نوع الهدف. يشير النوع المستهدف للتعبير إلى نوع البيانات الصحيح الذي يحتاجه التحويل البرمجي بناءً على المكان الذي يظهر فيه التعبير. على سبيل المثال ، هذا المثال:
static <T> list <T> leghlist () ؛ قائمة <Tring> listone = collections.emptylist () ؛
هنا ، القائمة <String> هو النوع الهدف ، لأن ما هو مطلوب هنا هو List<String> ، و Collections.emptyList() لإرجاع List<T> ، وبالتالي فإن المترجم هنا يجب أن يكون T String . هذا جيد في Java 7 و 8. ومع ذلك ، في Java 7 ، لا يمكن تجميعه بشكل طبيعي في الموقف التالي:
void ProcessStringList (قائمة <Tring> StringList) {// Process StringList} ProcessStringList (CollectionS.EmptyList ()) ؛في هذا الوقت ، ستعطي Java7 رسالة الخطأ هذه:
// قائمة <Object> لا يمكن تحويلها إلى قائمة <string>
السبب: Collections.emptyList() إرجاع List<T> ، وتتطلب T هنا نوعًا معينًا ، ولكن لأنه لا يمكن استنتاجه من إعلان الطريقة أن ما هو مطلوب هو String ، فإن المترجم يعطي T قيمة Object . من الواضح ، لا يمكن تحويل List<Object> إلى List<String>. لذلك في إصدار Java7 ، تحتاج إلى تسمية هذه الطريقة مثل هذه:
ProcessStringList (مجموعات. <string> leghlist ()) ؛
ومع ذلك ، في Java 8 ، نظرًا لإدخال مفهوم النوع المستهدف ، فمن الواضح أن ما يحتاجه المترجم هو List<String> (أي النوع الهدف هنا) ، وبالتالي فإن المترجم يثير أن t في List<T> يجب أن تكون String ، وبالتالي فإن وصف processStringList(Collections.emptyList()); على ما يرام.
استخدام الأنواع المستهدفة هو الأكثر وضوحا في تعبيرات لامدا.
لخص
حسنًا ، ما سبق هو بعض الأفكار الشخصية حول اشتقاق النوع في Java. باختصار ، فإن اشتقاق النوع المثالي بشكل متزايد هو إكمال بعض أعمال تحويل النوع التي يبدو أنها طبيعية ، ولكن يتم ترك كل هذه الأعمال للمترجم للاشتقاق التلقائي بدلاً من السماح للمطورين بعرضه. آمل أن يكون محتوى هذه المقالة مفيدًا للجميع في تعلم Java. إذا كان لديك أي أسئلة ، فيمكنك ترك رسالة للتواصل.