سلوك وقت الترجمة الغريب عند محاولة استخدام النوع البدائي في الأدوية العامة

StackOverflow https://stackoverflow.com/questions/2453359

سؤال

import java.lang.reflect.Array;

public class PrimitiveArrayGeneric {
    static <T> T[] genericArrayNewInstance(Class<T> componentType) {
        return (T[]) Array.newInstance(componentType, 0);
    }

    public static void main(String args[]) {
        int[] intArray;
        Integer[] integerArray;

        intArray = (int[]) Array.newInstance(int.class, 0);
        // Okay!

        integerArray = genericArrayNewInstance(Integer.class);
        // Okay!

        intArray = genericArrayNewInstance(int.class);
        // Compile time error:
           // cannot convert from Integer[] to int[]

        integerArray = genericArrayNewInstance(int.class);
        // Run time error:
           // ClassCastException: [I cannot be cast to [Ljava.lang.Object;
    }    
}

أحاول أن أفهم تمامًا كيفية عمل الأدوية العامة في Java.أصبحت الأمور غريبة بعض الشيء بالنسبة لي في المهمة الثالثة في المقتطف أعلاه:المترجم يشكو من ذلك Integer[] لا يمكن تحويلها إلى int[].العبارة صحيحة 100% بالطبع، لكني أتساءل لماذا المترجم هو تقديم هذه الشكوى.

إذا قمت بالتعليق على هذا السطر، واتبعت "اقتراح" المترجم كما في المهمة الرابعة، المترجم راض فعلا!!! الآن الكود يجمع على ما يرام!وهو أمر جنوني بالطبع، لأنه كما يوحي سلوك وقت التشغيل، int[] لا يمكن تحويلها إلى Object[] (وهو ما T[] يتم مسح النوع في وقت التشغيل).

لذلك سؤالي هو:لماذا "يقترح" المترجم أن أقوم بتعيينه Integer[] بدلا من المهمة الثالثة؟كيف يمكن للمترجم أن يصل إلى هذا الاستنتاج (الخاطئ!)؟


هناك الكثير من الالتباس في الإجابتين حتى الآن، لذلك قمت بإنشاء مثال محير آخر لتوضيح المشكلة الأساسية هنا:

public class PrimitiveClassGeneric {    
    static <T extends Number> T test(Class<T> c) {
        System.out.println(c.getName() + " extends " + c.getSuperclass());
        return (T) null;
    }
    public static void main(String args[]) {
        test(Integer.class);
        // "java.lang.Integer extends class java.lang.Number"

        test(int.class);
        // "int extends null"
    }
}

هل أنا الوحيد الذي يعتقد أنه من الجنون تمامًا أن يسمح المترجم بتجميع الكود أعلاه؟

لن يكون من غير المعقول الطباعة c.getSuperclass().getName() في الكود أعلاه، على سبيل المثال، منذ أن حددت ذلك T extends Number.بالطبع الآن getName() سوف يرمي NullPointerException متى c == int.class, ، منذ c.getSuperclass() == null.

وبالنسبة لي، هذا سبب وجيه جدًا لرفض تجميع الكود في المقام الأول.


ولعل الجنون المطلق:

    int.class.cast(null);

يجمع هذا الرمز و يعمل بشكل جيد.

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

المحلول

نوع من int.class يكون Class<Integer>, ، لذا genericArrayNewInstance() سيتم الاستدلال على العودة أ Integer[].لكن الوظيفة في الواقع تنشئ ملفًا int[], ، لذلك سيكون لها استثناء فئة عند إعادتها.في الأساس، يلقي ل T[] داخل الوظيفة غير مشروعة في هذه الحالة، لأن int[] ليس T[] (لا يمكن استخدام البدائيات في متغيرات النوع).لا يمكنك التعامل مع أنواع المصفوفات البدائية بشكل عام؛لذلك عليك إما أن يكون لديك طريقتك فقط قم بإرجاع النوع Object, أو يتعين عليك إنشاء طرق منفصلة للأنواع المرجعية والأنواع البدائية.

نصائح أخرى

بعض النقاط:

  1. البدائيون هم autoboxed إلى نظرائهم في الكائنات (الأغلفة) عند الحاجة
  2. المصفوفات البدائية هي كائنات، لذلك لا يتم وضعها تلقائيًا.
  3. لا يمكن للأدوية العامة استخدام البدائيات كمعلمات للنوع

للحصول على الأمثلة الخاصة بك، وهنا افتراضاتي:

في 3 يحدث autoboxing لـ معلمة النوع, ، ولكن لا يحدث ذلك للمصفوفة التي تم إرجاعها
في 4 يحدث autoboxing لـ معلمة النوع, ، ولكن لا يحدث ل حجة الطريقة, ، لذلك في الواقع int[] يتم إنشاؤه، ولكن Integer[] متوقع

قد لا يكون التشغيل التلقائي في حالة معلمة النوع دقيقًا com.autoboxing, ، ولكن هو شيء بنفس الفكرة.

تحديث: المثال الثاني الخاص بك لا يوجد لديه أي خطأ. int.class هو Class, لذلك ليس لدى المترجم أي سبب لرفضه.

وأنا أتفق مع الملصق الأصلي.هذا جنون.لماذا لا يمكنني استخدام البدائية مع العامة؟قد لا تكون هذه مشكلة المترجم، ولكنها مشكلة اللغة.من الخطأ ببساطة تخطي الأنواع البدائية من الأنواع العامة.

لهذا:

intArray = (int[]) Array.newInstance(int.class, 0);

int.class هو مجرد كائن فئة.لذلك لا بأس بالمرور."int" هو نوع، لذا فهو ليس جيدًا لأنه بدائي بشكل واضح.لا يعني أن هذه هي الطريقة "الأفضل" لإنشاء اللغة، فقط الالتزام باللغة.

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

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