سؤال

هل هناك أي عام عندما نقوم بإلقاء كائنات من نوع إلى آخر؟ أو المحول البرمجي يحل كل شيء فقط وليس هناك تكلفة في وقت التشغيل؟

هل هذه أشياء عامة ، أم أن هناك حالات مختلفة؟

على سبيل المثال ، لنفترض أن لدينا مجموعة من الكائن [] ، حيث قد يكون لكل عنصر نوع مختلف. لكننا نعرف دائمًا على وجه اليقين ، على سبيل المثال ، أن العنصر 0 هو مزدوج ، العنصر 1 هو سلسلة. (أعلم أن هذا تصميم خاطئ ، لكن دعنا نفترض أنه كان علي القيام بذلك.)

هل ما زالت معلومات نوع Java موجودة في وقت التشغيل؟ أو يتم نسيان كل شيء بعد التجميع ، وإذا فعلنا العناصر (المزدوجة) [0] ، فسنتبع المؤشر ونفسر تلك البايت الثمانية على أنها مزدوجة ، مهما كان ذلك؟

أنا غير واضح جدًا حول كيفية القيام بالأنواع في Java. إذا كان لديك أي توصية على الكتب أو المقالة ، شكرًا أيضًا.

هل كانت مفيدة؟

المحلول

هناك نوعان من الصب:

ضمني الصب ، عندما تقوم بإلقاء من نوع إلى نوع أوسع ، يتم تلقائيًا ولا يوجد أي تكاليف:

String s = "Cast";
Object o = s; // implicit casting

صريح الصب ، عندما تنتقل من نوع أوسع إلى واحد ضيق. لهذه الحالة ، يجب عليك استخدام الصب بشكل صريح مثل ذلك:

Object o = someObject;
String s = (String) o; // explicit casting

في هذه الحالة الثانية ، يوجد في وقت التشغيل في وقت التشغيل ، لأنه يجب التحقق من النوعين ، وفي حالة عدم قدر الإمكان ، يجب على JVM أن يرمي classcastexception.

مأخوذ من Javaworld: تكلفة الصب

يصب يستخدم للتحويل بين الأنواع - بين الأنواع المرجعية على وجه الخصوص ، لنوع عملية الصب التي نحن مهتمون بها هنا.

المهوى الصاعد العمليات (تسمى أيضًا تحويلات توسيع في مواصفات لغة Java) تحويل مرجع فئة فرعية إلى مرجع فئة الأجداد. عادةً ما تكون عملية الصب تلقائية ، لأنها دائمًا ما تكون آمنة ويمكن تنفيذها مباشرة بواسطة المترجم.

مسبل العمليات (تسمى أيضًا التحويلات الضيقة في مواصفات لغة Java) تحويل إشارة إلى فئة الأجداد إلى مرجع فئة فرعية. تنشئ عملية الصب هذه التنفيذ ، لأن Java تتطلب فحص الممثلين في وقت التشغيل للتأكد من أنه صالح. إذا لم يكن الكائن المشار إليه مثيلًا لنوع الهدف للنصوص أو الفئة الفرعية من هذا النوع ، فلا يُسمح بمحاولة الممثلين ويجب أن يرمي java.lang.classcastexception.

نصائح أخرى

لتنفيذ معقول لجافا:

يحتوي كل كائن على رأس يحتوي ، من بين أشياء أخرى ، على مؤشر إلى نوع وقت التشغيل (على سبيل المثال Double أو String, ، لكن لا يمكن أن يكون أبدًا CharSequence أو AbstractList). على افتراض أن برنامج التحويل البرمجي لوقت التشغيل (النقطة الساخنة عمومًا في حالة Sun) لا يمكن تحديد النوع بشكل ثابت الذي يجب إجراء بعض التحقق من خلال رمز الجهاز الذي تم إنشاؤه.

أولاً ، يجب قراءة هذا المؤشر إلى نوع وقت التشغيل. هذا ضروري لاستدعاء طريقة افتراضية في موقف مماثل على أي حال.

للالتصاق بنوع الفصل ، من المعروف بالضبط عدد الفئات الفائقة الموجودة حتى تضرب java.lang.Object, ، لذلك يمكن قراءة النوع في إزاحة ثابتة من مؤشر النوع (في الواقع الثمانية الأولى في نقطة ساخنة). مرة أخرى ، هذا مماثل لقراءة مؤشر الطريقة لطريقة افتراضية.

ثم تحتاج قيمة القراءة فقط إلى مقارنة بين النوع الثابت المتوقع من الممثلين. اعتمادًا على بنية مجموعة التعليمات ، ستحتاج تعليمات أخرى إلى الفرع (أو الخطأ) في فرع غير صحيح. لدى ISAs مثل ARM 32 بت تعليمات مشروطة وقد تكون قادرة على الحصول على المسار الحزين عبر المسار السعيد.

الواجهات أكثر صعوبة بسبب ميراث متعددة للواجهة. بشكل عام ، يتم تخزين اثنين من الممثلين إلى الواجهات في نوع وقت التشغيل. في الأيام الأولى (منذ أكثر من عقد من الزمان) ، كانت واجهات بطيئة بعض الشيء ، لكن هذا لم يعد ذا صلة.

نأمل أن ترى أن هذا النوع من الأشياء غير ذي صلة إلى حد كبير بالأداء. رمز المصدر الخاص بك أكثر أهمية. فيما يتعلق بالأداء ، فإن أكبر نجاح في السيناريو الخاص بك هو عرضة لذاكرة التخزين المؤقت من مطاردة مؤشرات الكائنات في كل مكان (ستكون معلومات النوع بالطبع شائعة).

على سبيل المثال ، لنفترض أن لدينا مجموعة من الكائن [] ، حيث قد يكون لكل عنصر نوع مختلف. لكننا نعرف دائمًا على وجه اليقين ، على سبيل المثال ، أن العنصر 0 هو مزدوج ، العنصر 1 هو سلسلة. (أعلم أن هذا تصميم خاطئ ، لكن دعنا نفترض أنه كان علي القيام بذلك.)

لا يلاحظ المترجم أنواع العناصر الفردية للمجموعة. إنه ببساطة يتحقق من أن نوع كل عنصر من العناصر يمكن تعيينه لنوع عنصر الصفيف.

هل ما زالت معلومات نوع Java موجودة في وقت التشغيل؟ أو يتم نسيان كل شيء بعد التجميع ، وإذا فعلنا العناصر (المزدوجة) [0] ، فسنتبع المؤشر ونفسر تلك البايت الثمانية على أنها مزدوجة ، مهما كان ذلك؟

يتم الاحتفاظ ببعض المعلومات في وقت التشغيل ، ولكن ليس الأنواع الثابتة للعناصر الفردية. يمكنك معرفة ذلك من النظر إلى تنسيق ملف الفصل.

من الممكن نظريًا أن يستخدم برنامج التحويل البرمجي JIT "تحليل الهروب" للقضاء على عمليات التحقق من النوع غير الضروري في بعض المهام. ومع ذلك ، فإن القيام بذلك إلى الدرجة التي تقترحها سيكون خارج حدود التحسين الواقعي. إن مردود تحليل أنواع العناصر الفردية سيكون صغيرًا جدًا.

علاوة على ذلك ، يجب ألا يكتب الناس رمز التطبيق مثل هذا على أي حال.

يتم استدعاء تعليمات رمز البايت لأداء الصب في وقت التشغيل checkcast. يمكنك تفكيك رمز Java باستخدام javap لمعرفة التعليمات التي يتم إنشاؤها.

بالنسبة للصفائف ، تحتفظ Java بالمعلومات في وقت التشغيل. في معظم الأوقات ، سيقوم برنامج التحويل البرمجي بالتقاط أخطاء لك ، ولكن هناك حالات ستواجه فيها ArrayStoreException عند محاولة تخزين كائن في صفيف ، لكن النوع لا يتطابق (ولم يمسكه التحويل البرمجي). ال مواصفات لغة جافا يعطي المثال التالي:

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
class Test {
    public static void main(String[] args) {
        ColoredPoint[] cpa = new ColoredPoint[10];
        Point[] pa = cpa;
        System.out.println(pa[1] == null);
        try {
            pa[0] = new Point();
        } catch (ArrayStoreException e) {
            System.out.println(e);
        }
    }
}

Point[] pa = cpa صالح منذ ذلك الحين ColoredPoint هي فئة فرعية من نقطة ، ولكن pa[0] = new Point() غير صالح.

يعارض ذلك الأنواع العامة ، حيث لا توجد معلومات عن النوع في وقت التشغيل. المترجم يدرج checkcast التعليمات عند الضرورة.

هذا الاختلاف في الكتابة للأنواع العامة والصفائف يجعل من غير المناسب في كثير من الأحيان خلط المصفوفات والأنواع العامة.

من الناحية النظرية ، هناك إدخال النفقات العامة. ومع ذلك ، فإن JVMs الحديثة ذكية. كل تنفيذ مختلف ، لكن ليس من غير المعقول افتراض وجود تنفيذ قام JIT بتحسين اختبارات الإلقاء عندما يتمكن من ضمان أنه لن يكون هناك صراع. أما بالنسبة إلى JVMs المحددة ، فإنني لم أستطع إخبارك. يجب أن أعترف أنني أود معرفة تفاصيل تحسين JIT بنفسي ، ولكن هذه هي لمهندسي JVM للقلق.

الأخلاقية للقصة هي كتابة رمز مفهوم أولاً. إذا كنت تعاني من تباطؤ ، وملف تعريف وتحديد مشكلتك. الاحتمالات جيدة أنه لن يكون بسبب الصب. لا تضحي أبدًا رمزًا نظيفًا وآمنًا في محاولة لتحسينه حتى تعرف أنك بحاجة إليها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top