instantiationException على newinstance من فئة مجهولة المصدر
-
27-09-2019 - |
سؤال
تحديث: هذا أكثر أو أقل أ مغفل, ، واتضح أن يكون سحر المترجم يضيف مُنشئًا لتمريره في المتغير المحلي في Build2.
إعطاء واجهة مثل هذه:
public interface IFoo {
public int get();
}
يطبع الكود أدناه 1 و 1 و 2 ثم يلقي استثناء عند محاولة الاتصال getClass (). newInstance () على القيمة التي تم إرجاعها بواسطة Build2 ، ولكن لا عند الاتصال بنفسها على القيمة التي تم إرجاعها من Build1. أي أفكار لماذا؟
public class Foo {
public static IFoo build1() {
return new IFoo() { public int get() { return 1; } };
}
public static IFoo build2(final int v) {
return new IFoo() { public int get() {return v;} };
}
public static void main(String[] args) throws Exception {
IFoo foo, bar;
foo = build1();
System.out.println(foo.get());
bar = foo.getClass().newInstance();
System.out.println(bar.get());
foo = build2(2);
System.out.println(foo.get());
bar = foo.getClass().newInstance();
System.out.println(bar.get());
}
}
يشير مصحح الأخطاء الخاص بي إلى أنه في مكالمة NewInstance () ، يقوم GetConstructor0 بإلقاء NosuchmethodException.
المحلول
هذا ما يحدث:
newInstance()
يتطلب مُنشئًا لاغية- عندما تقوم بإنشاء فئة مجهولة المصدر تصل إلى ملف
final
متغير ، يتم إنشاء حقل ضمنيًا في الواقع للاحتفاظ بهذه القيمة ، والذي يتم تمريره في البداية إلى مُنشئه الضمني - وهكذا ، و
IFoo
أنشئت فيbuild2
ليس لديه في الواقع مُنشئ لا لبس
إليك مقتطف لإظهار ما يحدث:
import java.lang.reflect.*;
public class Foo {
interface IFoo { public int get(); }
public static IFoo build2(final int v) {
return new IFoo() { public int get() {return v;} };
}
public static void main(String[] args) throws Exception {
Class<?> klazz = build2(42).getClass();
for (Constructor<?> c : klazz.getDeclaredConstructors()) {
System.out.println(c);
}
// prints: Foo$1(int)
}
}
إنها تظهر أن Foo$1
(الاسم الثنائي المعين للمجهول IFoo
الطبقة) يحتوي على مُنشئ واحد فقط ، ويستغرق int
. هكذا يمكن return v
, ، لأن ما تم إرجاعه هو في الواقع كل ما يتم تعيينه إلى الحقل الذي تم إنشاؤه ضمنيًا بواسطة هذا المنشئ الذي تم إنشاؤه ضمنيًا.
من المفيد إلغاء تجميع Foo$1
(باستخدام مثل javap -c
) لمعرفة ما يتم إنشاء رمز bytecode. سترى ذلك في الحقيقة هذا ما يحدث عندما أ final
يتم الوصول إلى المتغير بواسطة فئة مجهولة.