سؤال

في أي ظروف يمكن أن تحدث classcastexception في الكود أدناه:

import java.util.Arrays;
import java.util.List;

public class Generics {

    static List getObjects() {
        return Arrays.asList(1, 2, 3);
    }

    public static void main(String[] args) {
        List<String> list = getObjects();
        for (Object o : list) { // ClassCastException?
            System.out.println(o);
        }
    }
}

كان لدينا حالة مماثلة في بيئة الإنتاج (الممارسة السيئة ، وأنا أعلم) وقدم العميل سجلًا مع ClassCastException على السطر مع التعليق ، لكن لا يمكنني إعادة إنتاجه. أي أفكار؟

أعلم أن JVM ينشئ تكرارًا في الخلفية عند استخدام Foreach ، ولكن هل يمكن أن يقوم بإنشاء جهاز تكرار خام في بعض الحالات وواحد محدد في حالات أخرى؟

تحديث:لقد ألقيت إلقاء نظرة على رمز Bytecder الذي تم إنشاؤه وعلى Windows ، باستخدام JDK 1.6.0_21-B07 لا checkcast صنع. مثير للإعجاب :)

هذه هي الطريقة الرئيسية:

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #34; //Method getObjects:()Ljava/util/List;
   3:   astore_1
   4:   aload_1
   5:   invokeinterface #36,  1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
   10:  astore_3
   11:  goto    28
   14:  aload_3
   15:  invokeinterface #42,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   20:  astore_2
   21:  getstatic   #48; //Field java/lang/System.out:Ljava/io/PrintStream;
   24:  aload_2
   25:  invokevirtual   #54; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   28:  aload_3
   29:  invokeinterface #60,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   34:  ifne    14
   37:  return

شكرا جميع على الإجابات!

تحديث 2: لقد تضللت مع Eclipse IDE التي تستخدمها المترجم الخاص لذلك في الواقع ، فإن رمز Bytecode أعلاه هو الذي تم إنشاؤه باستخدام برنامج التحويل البرمجي الكسوف. نظرة هنا كيفية تجميع الكود يدويًا مع Eclipse. في الختام ، يولد برنامج التحويل البرمجي Eclipse رمز بايت مختلف من برنامج التحويل البرمجي للشمس في بعض الحالات ، بغض النظر عن المنصة ، وهي الحالة الموضحة هنا واحدة.

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

المحلول

لا ينبغي أن هذا الرمز دائماً اقذف ال ClassCastException؟ إنه بالنسبة لي باستخدام برنامج التحويل البرمجي Sun Java 6 ووقت التشغيل (على Linux). أنت تلقي Integerق Stringس. سيكون التكرار الذي تم إنشاؤه Iterator<String>, ، ولكن بعد ذلك يحاول الوصول إلى العنصر الأول ، وهو أمر Integer, ، وهكذا فشل.

يصبح هذا أكثر وضوحًا إذا قمت بتغيير صفيفك مثل ذلك:

return Arrays.asList("one", 2, 3);

الآن تعمل الحلقة فعليًا للعنصر الأول ، لأن العنصر الأول هو String ونحن نرى الإخراج. ثم Iterator<String> يفشل في الثانية ، لأنها ليست سلسلة.

يعمل الكود الخاص بك إذا كنت تستخدم فقط عام List بدلا من واحد معين:

List list = getObjects();
for (Object o : list) {
    System.out.println(o);
}

... أو بالطبع ، إذا كنت تستخدم List<Integer>, ، لأن المحتويات Integerس. ما تفعله الآن يؤدي إلى تحذير مترجم - Note: Generics.java uses unchecked or unsafe operations.- ولسبب وجيه.

هذا التعديل يعمل أيضًا:

for (Object o : (List)list)

... من المفترض لأنك في هذه المرحلة تتعامل مع Iterator, ، ليس Iterator<String>.

قال Bozho إنه لا يرى هذا الخطأ على Windows XP (لم يذكر أي برنامج التحويل البرمجي ووقت التشغيل ، لكنني أخمن Sun's) ، وأنت تقول أنك لا ترى ذلك (أو غير موثوق) ، لذلك من الواضح أن هناك البعض حساسية التنفيذ هنا ، ولكن خلاصة القول هي: لا تستخدم List<String> للتفاعل مع أ List من Integerس. :-)

هذا هو الملف الذي أقوم بتجميعه:

import java.util.Arrays;
import java.util.List;

public class Generics {

    static List getObjects() {
        return Arrays.asList("one", 2, 3);
    }

    public static void main(String[] args) {
        List<String> list = getObjects();
        for (Object o : list) { // ClassCastException?
            System.out.println(o);
        }
    }
}

ها هي المجموعة:

tjc@forge:~/temp$ javac Generics.java 
Note: Generics.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

ها هو المدى:

tjc@forge:~/temp$ java Generics 
one
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at Generics.main(Generics.java:12)

السطر 12 هو for بيان. لاحظ أنه قام بإخراج العنصر الأول ، لأنني غيرت ذلك إلى أ String. لم يخرج الآخرين. (وقبل أن أجري هذا التغيير ، فشل على الفور.)

إليك المترجم الذي أستخدمه:

tjc@forge:~/temp$ which javac
/usr/bin/javac
tjc@forge:~/temp$ ll /usr/bin/javac
lrwxrwxrwx 1 root root 23 2010-09-30 16:37 /usr/bin/javac -> /etc/alternatives/javac*
tjc@forge:~/temp$ ll /etc/alternatives/javac
lrwxrwxrwx 1 root root 33 2010-09-30 16:37 /etc/alternatives/javac -> /usr/lib/jvm/java-6-sun/bin/javac*

إليكم التفكيك ، الذي يوضح checkcast:

tjc@forge:~/temp$ javap -c Generics
Compiled from "Generics.java"
public class Generics extends java.lang.Object{
public Generics();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

static java.util.List getObjects();
  Code:
   0:   iconst_3
   1:   anewarray   #2; //class java/io/Serializable
   4:   dup
   5:   iconst_0
   6:   ldc #3; //String one
   8:   aastore
   9:   dup
   10:  iconst_1
   11:  iconst_2
   12:  invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   15:  aastore
   16:  dup
   17:  iconst_2
   18:  iconst_3
   19:  invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   22:  aastore
   23:  invokestatic    #5; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   26:  areturn

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #6; //Method getObjects:()Ljava/util/List;
   3:   astore_1
   4:   aload_1
   5:   invokeinterface #7,  1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
   10:  astore_2
   11:  aload_2
   12:  invokeinterface #8,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   17:  ifeq    40
   20:  aload_2
   21:  invokeinterface #9,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   26:  checkcast   #10; //class java/lang/String
   29:  astore_3
   30:  getstatic   #11; //Field java/lang/System.out:Ljava/io/PrintStream;
   33:  aload_3
   34:  invokevirtual   #12; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   37:  goto    11
   40:  return

}

مرة أخرى ، رغم ذلك ، يجب أن يكون خلاصة القول: لا تستخدم أ List<String> للتفاعل مع أ List التي تحتوي على أشياء ليست Stringس. :-)

نصائح أخرى

لا يمكنني إعادة إنتاجه أيضًا ، لكنني اكتشفت الأخطاء التالية التي يجب تصحيحها:

  • صنع getObjects() إرجاع List<Integer>, ، بدلا من النوع الخام
  • لا تتوقع List<String>, ، لكن أ List<Integer> في حين أن
  • عند التكرار ، حلقة for (Integer o : list)

المشكلة هي الطريقة static List getObjects() { إرجاع عام (غير معلمة) List. وأنت تقوم بتعيينها إلى List<String>. يجب أن يعطي هذا الخط تحذيرًا للمترجم ، ويجب أن يشير ذلك إلى مشكلة.

هذا الجزء :

List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
    System.out.println(o);
}

سيتم تبسيطها

List<String> list = getObjects();
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    Object o = iterator.next();
    System.out.println(o);
}

لكن تنفيذ التكرار سيحاول الإلقاء عند الاتصال next() طريقة إرسال المحتوى بواسطة Iterator في String.

لهذا السبب لديك CCE.

حلول :

إما استخدام الأدوية الجيلية في كل مكان أو لا تستخدمها ، ولكن من المهم حقًا أن تكون متسقًا. إذا كنت قد عادت List<Integer> أو أ List<? super Integer> كنت قد رأيت هذه المشكلة في وقت التجميع.

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