الأدوية العامة والمصفوفات وClassCastException
-
21-08-2019 - |
سؤال
أعتقد أن هناك شيئًا خفيًا يحدث هنا ولا أعرف عنه شيئًا.خذ بعين الاعتبار ما يلي:
public class Foo<T> {
private T[] a = (T[]) new Object[5];
public Foo() {
// Add some elements to a
}
public T[] getA() {
return a;
}
}
لنفترض أن طريقتك الرئيسية تحتوي على ما يلي:
Foo<Double> f = new Foo<Double>();
Double[] d = f.getA();
سوف تحصل على CastClassException
مع الرسالة java.lang.Object
لا يمكن طرحه java.lang.Double
.
يمكن لأحد أن يقول لي لماذا؟فهمي ل ClassCastException
هو أنه يتم طرحه عند محاولة إرسال كائن إلى نوع لا يمكن إرساله.أي إلى فئة فرعية ليست مثالًا لها (على حد تعبير الوثائق).على سبيل المثال:
Object o = new Double(3.);
Double d = (Double) o; // Working cast
String s = (String) o; // ClassCastException
ويبدو أنني أستطيع أن أفعل هذا.لو a
كان مجرد T
بدلا من مصفوفة T[]
, ، يمكننا الحصول على a
ويلقي بها دون مشكلة.لماذا تكسر المصفوفات هذا؟
شكرًا.
المحلول
Foo<Double> f = new Foo<Double>();
عند استخدام هذا الإصدار من الفئة العامة Foo، ثم لمتغير العضو a
, ، يأخذ المترجم بشكل أساسي هذا السطر:
private T[] a = (T[]) new Object[5];
واستبدال T
مع Double
للحصول على هذا:
private Double[] a = (Double[]) new Object[5];
لا يمكنك الإرسال من كائن إلى مزدوج، ومن هنا جاء ClassCastException.
تحديث وتوضيح: في الواقع، بعد تشغيل بعض أكواد الاختبار، أصبح ClassCastException أكثر دقة من هذا.على سبيل المثال، هذه الطريقة الرئيسية ستعمل بشكل جيد دون أي استثناء:
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
System.out.println(f.getA());
}
المشكلة تحدث عند محاولة التعيين f.getA()
إلى مرجع النوع Double[]
:
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
Double[] a2 = f.getA(); // throws ClassCastException
System.out.println(a2);
}
وذلك لأن معلومات النوع حول متغير العضو a
يتم مسحه في وقت التشغيل.الأدوية العامة توفر فقط سلامة النوع في وقت الترجمة (كنت أتجاهل هذا بطريقة أو بأخرى في رسالتي الأولية).لذا فإن المشكلة ليست كذلك
private T[] a = (T[]) new Object[5];
لأنه في وقت التشغيل هذا الرمز هو حقا
private Object[] a = new Object[5];
المشكلة تحدث عندما نتيجة الأسلوب getA()
, ، والذي يقوم في وقت التشغيل بإرجاع ملف Object[]
, ، يتم تعيينه إلى مرجع من النوع Double[]
- يطرح هذا البيان ClassCastException لأنه لا يمكن تحويل الكائن إلى Double.
تحديث 2:للإجابة على سؤالك الأخير "لماذا تكسر المصفوفات هذا؟" الجواب هو أن مواصفات اللغة لا تدعم إنشاء مجموعة عامة. انظر هذا المنتدى لمزيد من المعلومات - لكي يكون متوافقًا مع الإصدارات السابقة، لا يُعرف أي شيء عن نوع T في وقت التشغيل.
نصائح أخرى
وربما تكون هناك بعض الأخطاء الصغيرة في التفسير @ mattb ل.
والخطأ هو لا م>
<اقتباس فقرة> لا يمكن أن يلقيوjava.lang.Object إلى java.lang.Double.
اقتباس فقرة>ومن:
<اقتباس فقرة>[Ljava.lang.Object. لا يمكن أن يلقي ل[Ljava.lang.Double
اقتباس فقرة>وو[L يعني صفيف. وهذا هو، والخطأ هو أن <م> مجموعة م> الأجسام لا يمكن أن يلقي على مجموعة من مزدوجة. هذا هو نفس القضية على النحو التالي:
Object[] oarr = new Object[10];
Double[] darr = (Double[]) oarr;
ومن الواضح أن لا يسمح هذا.
لمشكلتك إنشاء صفائف typesafe، بديل آخر هو ما عدا فئة الكائن في الحرف الأول واستخدام Array.newInstance:
import java.lang.reflect.Array;
class Foo<T> {
private T[] a ;
public Foo(Class<T> tclass) {
a = (T[]) Array.newInstance(tclass, 5);
}
public T[] getA() {
return a;
}
public static <T> Foo<T> create(Class<T> tclass) {
return new Foo<T>(tclass);
}
}
class Array1
{
public static final void main(final String[] args) {
Foo<Double> f = Foo.create(Double.class);
Double[] d = f.getA();
}
}
وmatt ب: شكرا للإجابة! مفيدة جدا.
ولقد وجدت حلا للراغبين: إعطاء طريقة جيتا مجموعة تهيئته لتجميع. وبهذه الطريقة معلومات نوع غير متوفرة.
public class Foo<T> {
private T[] a = (T[]) new Object[5];
public Foo() {
// Add some elements to a
}
public T[] getA(T[] holdA) {
// Check whether holdA is null and handle it...then:
holdA = (T[]) Array.newInstance(holdA.getClass().getComponentType(), a.length);
System.arraycopy(a, 0, holdA, 0, a.length);
return holdA;
}
}
وبعد ذلك لطريقة الرئيسي الخاص بك:
public static void main(String[] args) {
Foo<Double> f = new Foo<Double>();
Double[] a2 = new Double[1];
a2 = f.getA(a2);
}