Đây là câu hỏi rất đơn giản với câu trả lời rất phức tạp. Hãy chịu với tôi khi tôi cố giải thích nó.
Nhìn vào mã nguồn nơi ngoại lệ xảy ra trong Class
(Tôi không chắc chắn lý do tại sao stack trace của bạn không cung cấp cho các số dòng trong Class
):
try
{
Class[] empty = {};
final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
// removed some code that was not relevant
}
catch (NoSuchMethodException e)
{
throw new InstantiationException(getName());
}
bạn thấy rằng NoSuchMethodException
đang được được gọi lại là InstantiationException
. Điều này có nghĩa là không có một hàm tạo no-arg cho kiểu lớp là object2
.
Đầu tiên, loại nào là object2
? Với mã
System.out.println("object2 class: " + object2.getClass());
chúng ta thấy rằng
object2 lớp: lớp junk.NewMain $ 1
đó là chính xác (tôi chạy mẫu mã trong gói rác, lớp NewMain).
Sau đó, các nhà thầu của junk.NewMain$1
là gì?
Class obj2Class = object2.getClass();
try
{
Constructor[] ctors = obj2Class.getDeclaredConstructors();
for (Constructor cc : ctors)
{
System.out.println("my ctor is " + cc.toString());
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
mà cho chúng ta
ctor của tôi là junk.NewMain $ 1 (java.util.Calendar)
Vì vậy, lớp ẩn danh của bạn đang tìm kiếm một Calendar
để được thông qua trong. Điều này sau đó sẽ phù hợp với bạn:
Object newObj = ctors[0].newInstance(Calendar.getInstance());
Nếu bạn có thứ gì đó thích e này:
final String finalString = "I'm final :)";
final Integer finalInteger = new Integer(30);
final Calendar calendar = Calendar.getInstance();
Object object2 = new Object()
{
{
System.out.println("Instance initializing block");
System.out.println(finalString);
System.out.println("My integer is " + finalInteger);
System.out.println(calendar.getTime().toString());
}
private void hiddenMethod()
{
System.out.println("Use reflection to find me :)");
}
};
sau đó kêu gọi của Mẹ để newInstance
sẽ không hoạt động vì không có đủ lý lẽ trong ctor, bởi vì bây giờ nó muốn:
ctor của tôi là junk.NewMain $ 1 (java. lang.Integer, java.util.Calendar)
Nếu tôi sau đó nhanh chóng rằng với
Object newObj = ctors[0].newInstance(new Integer(25), Calendar.getInstance());
một peek bên trong bằng cách sử dụng trình gỡ lỗi cho thấy rằng finalInteger
là 25 và không phải là giá trị cuối cùng 30.
Mọi thứ hơi phức tạp vì bạn đang làm tất cả những điều trên trong ngữ cảnh tĩnh. Nếu bạn mất tất cả các mã của bạn ở trên và di chuyển nó vào một phương pháp không tĩnh như vậy (hãy nhớ, lớp học của tôi là junk.NewMain):
public static void main(String[] args)
{
NewMain nm = new NewMain();
nm.doIt();
}
public void doIt()
{
final String finalString = "I'm final :)";
// etc etc
}
bạn sẽ tìm thấy ctor cho lớp bên trong của bạn bây giờ (loại bỏ thêm Integer tài liệu tham khảo của tôi):
ctor của tôi là junk.NewMain $ 1 (junk.NewMain, java.util.Calendar)
Các Java Language Specification, phần 15.9.3 giải thích nó theo cách này:
Nếu C là một lớp vô danh, và các lớp cha trực tiếp của C, S, là một lớp bên trong, sau đó:
- Nếu S là một lớp địa phương và S xảy ra trong một bối cảnh tĩnh, sau đó các đối số trong danh sách đối số, nếu có, là các đối số cho hàm tạo, theo thứ tự chúng xuất hiện trong biểu thức.
- Nếu không, trường hợp kèm theo ngay lập tức của i đối với S là đối số đầu tiên cho hàm tạo, tiếp theo là đối số trong danh sách đối số của biểu thức tạo kiểu , nếu có, theo thứ tự chúng xuất hiện trong cách diễn đạt.
Tại sao trình tạo ẩn danh lại lấy đối số?
Vì bạn không thể tạo hàm tạo cho lớp bên trong vô danh, khối khởi tạo thể hiện phục vụ mục đích đó (hãy nhớ, bạn chỉ có một thể hiện của lớp bên trong ẩn danh đó). VM không có kiến thức về lớp bên trong vì trình biên dịch tách tất cả mọi thứ ra thành các lớp riêng lẻ (ví dụ: junk.NewMain $ 1). Ctor cho lớp đó chứa nội dung của trình khởi tạo thể hiện.
này được explayed bởi JLS 15.9.5.1 Anonymous Constructors:
... các nhà xây dựng vô danh có một tham số chính thức cho mỗi tranh luận thực tế để biểu hiện sự sáng tạo lớp dụ trong đó C là tuyên bố.
Trình khởi chạy thể hiện của bạn có tham chiếu đến đối tượng Calendar
. Trình biên dịch sẽ lấy giá trị thời gian chạy đó vào lớp bên trong của bạn như thế nào (được tạo ra như một lớp cho VM), ngoại trừ thông qua hàm tạo?
Cuối cùng (yay), câu trả lời cho câu hỏi đốt cuối cùng. Tại sao nhà xây dựng không yêu cầu String
? Bit cuối cùng của JLS 3.10.5 giải thích rằng:
Strings tính bằng biểu thức hằng số được tính tại thời gian biên dịch và sau đó đối xử như thể chúng là literals.
Nói cách khác, giá trị String
của bạn được biết tại thời gian biên dịch vì nó là chữ để nó không cần phải là một phần của hàm tạo ẩn danh. Để chứng minh trường hợp này, chúng tôi sẽ kiểm tra câu lệnh tiếp theo trong JLS 3.10.5:
Các chuỗi được tính bằng cách nối vào thời gian chạy mới được tạo và do đó khác biệt.
Thay đổi mã của bạn thusly:
String str1 = "I'm";
String str2 = " final!";
final String finalString = str1 + str2
và bạn sẽ tìm ctor của bạn bây giờ là (trong bối cảnh không tĩnh):
ctor của tôi là junk.NewMain $ 1 (junk.NewMain, java.lang.String, java.util.Calendar)
Phew. Tôi hy vọng điều này có ý nghĩa và hữu ích. Tôi đã học được rất nhiều, đó là chắc chắn!
Điều gì xảy ra nếu Lịch instnace không kết thúc? – SJuan76
@ SJuan76. ... thì bạn sẽ không thể truyền nó vào lớp bên trong vô danh. –