2015-09-28 13 views
7

Khi tôi tháo rời một enum với javap, các đối số hàm tạo ngầm của enum dường như bị thiếu và tôi không thể hiểu tại sao.Enum tháo rời với javap không hiển thị đối số hàm tạo

Dưới đây là một enum:

enum Foo { X } 

tôi biên dịch và tháo rời này (trên Java 8u60) với lệnh này:

javac Foo.java && javap -c -p Foo 

Và đây là kết quả tôi nhận được:

final class Foo extends java.lang.Enum<Foo> { 
    public static final Foo X; 

    private static final Foo[] $VALUES; 

    public static Foo[] values(); 
    Code: 
     0: getstatic  #1     // Field $VALUES:[LFoo; 
     3: invokevirtual #2     // Method "[LFoo;".clone:()Ljava/lang/Object; 
     6: checkcast  #3     // class "[LFoo;" 
     9: areturn 

    public static Foo valueOf(java.lang.String); 
    Code: 
     0: ldc   #4     // class Foo 
     2: aload_0 
     3: invokestatic #5     // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 
     6: checkcast  #4     // class Foo 
     9: areturn 

    private Foo(); // <--- here 
    Code: 
     0: aload_0 
     1: aload_1 
     2: iload_2 
     3: invokespecial #6     // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 
     6: return 

    static {}; 
    Code: 
     0: new   #4     // class Foo 
     3: dup 
     4: ldc   #7     // String X 
     6: iconst_0 
     7: invokespecial #8     // Method "<init>":(Ljava/lang/String;I)V 
     10: putstatic  #9     // Field X:LFoo; 
     13: iconst_1 
     14: anewarray  #4     // class Foo 
     17: dup 
     18: iconst_0 
     19: getstatic  #9     // Field X:LFoo; 
     22: aastore 
     23: putstatic  #1     // Field $VALUES:[LFoo; 
     26: return 
} 

Sự nhầm lẫn của tôi là với hàm tạo riêng tư được sử dụng để khởi tạo từng hằng số enum. Việc tháo gỡ cho thấy rằng nó không có đối số (private Foo();), nhưng nó chắc chắn không có đối số. Ví dụ: bạn có thể thấy các hướng dẫn load đọc tên hằng số enum được truyền và thứ tự, cũng như con trỏ this và chuyển chúng đến the superclass constructor, yêu cầu chúng. Mã trong khối khởi tạo tĩnh cũng cho thấy rằng nó đẩy các đối số đó vào ngăn xếp trước khi gọi hàm tạo.

Bây giờ tôi sẽ giả định đây chỉ là một lỗi khó hiểu trong javap, nhưng khi tôi biên dịch chính xác enum cùng với trình biên dịch của Eclipse và tháo rời rằng việc sử dụng javap, các nhà xây dựng là hoàn toàn giống nhau, ngoại trừ các đối số hiển thị:

final class Foo extends java.lang.Enum<Foo> { 
    public static final Foo X; 

    private static final Foo[] ENUM$VALUES; 

    static {}; 
    Code: 
     0: new   #1     // class Foo 
     3: dup 
     4: ldc   #12     // String X 
     6: iconst_0 
     7: invokespecial #13     // Method "<init>":(Ljava/lang/String;I)V 
     10: putstatic  #17     // Field X:LFoo; 
     13: iconst_1 
     14: anewarray  #1     // class Foo 
     17: dup 
     18: iconst_0 
     19: getstatic  #17     // Field X:LFoo; 
     22: aastore 
     23: putstatic  #19     // Field ENUM$VALUES:[LFoo; 
     26: return 

    private Foo(java.lang.String, int); // <--- here 
    Code: 
     0: aload_0 
     1: aload_1 
     2: iload_2 
     3: invokespecial #23     // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V 
     6: return 

    public static Foo[] values(); 
    Code: 
     0: getstatic  #19     // Field ENUM$VALUES:[LFoo; 
     3: dup 
     4: astore_0 
     5: iconst_0 
     6: aload_0 
     7: arraylength 
     8: dup 
     9: istore_1 
     10: anewarray  #1     // class Foo 
     13: dup 
     14: astore_2 
     15: iconst_0 
     16: iload_1 
     17: invokestatic #27     // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V 
     20: aload_2 
     21: areturn 

    public static Foo valueOf(java.lang.String); 
    Code: 
     0: ldc   #1     // class Foo 
     2: aload_0 
     3: invokestatic #35     // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 
     6: checkcast  #1     // class Foo 
     9: areturn 
} 

câu hỏi của tôi là: những gì chất là khác nhau giữa một enum javac biên dịch và một enum Eclipse biên soạn gây javap để không hiển thị các đối số nhà xây dựng cho enum javac biên dịch? Và sự khác biệt này là một lỗi (trong javap, trong javac, hoặc Eclipse)?

+0

Khi đoán, cài đặt cờ '-g' (kiểm soát việc tạo thông tin gỡ lỗi). – CPerkins

+0

@CPerkins Giả thuyết tốt nhưng tôi đã so sánh '-g' (tạo tất cả thông tin gỡ lỗi) và' -g: none' (không tạo ra thông tin gỡ lỗi) và dường như không tạo ra sự khác biệt nào. – Boann

+0

Bạn đã thử cờ '-v' (tiết) chưa? Nó ít nhất sẽ cho bạn thấy bộ mô tả của hàm tạo. Tôi đoán là 'javac' đánh dấu các tham số đầu tiên' MANDATED', có thể làm cho 'javap' bỏ qua chúng. – Clashsoft

Trả lời

3

Các tham số và kiểu trả về của phương thức bên trong tệp lớp được mô tả bằng method descriptor.

Với sự ra đời của Generics trong 1.5. thông tin bổ sung đã được đưa vào định dạng tệp lớp học, số method signature.

"Bộ mô tả phương pháp" được sử dụng để mô tả phương pháp sau khi xóa loại, "chữ ký phương thức" bổ sung chứa thông tin loại chung.

Bây giờ javap in chữ ký phương thức (có chứa thêm thông tin) và khi cờ -v được đặt, nó cũng in bộ mô tả.

Điều này cho thấy rằng cũng là hàm tạo của lớp enum javac tạo ra một bộ mô tả phương pháp với các kiểu tham số Stringint. Bây giờ nó cũng rõ ràng lý do tại sao cả hai Elipse và javac tạo ra mã làm việc. Cả hai đều gọi hàm tạo riêng với các đối số Stringint.

Điều gì vẫn cần được giải thích: Tại sao javac tạo chữ ký khác với bộ mô tả ở tất cả - không có generics nào được tham gia?

Dù sao, các hành vi của javac liên quan đến việc xây dựng enum đã gây ra other troubles và một báo cáo lỗi cho javacfiled:

Không cần cho xây dựng một tuyên bố enum để có một thuộc tính Chữ ký lưu trữ một phương pháp chữ ký nếu 1) hàm tạo không phải là chung chung và 2) các kiểu tham số chính thức của nó không phải là các loại tham số cũng như các biến kiểu. Đó là lỗi nếu javac mong đợi một thuộc tính Chữ ký cho hàm tạo được viết ở trên.

Các nhận xét sau và phân loại trường hợp cho thấy đây là lỗi thực sự trong javac.

+0

Cảm ơn, điều này giải thích sự khác biệt. Nó chỉ ra rằng javac không bao gồm * rõ ràng * xác định các thông số hàm tạo enum trong chữ ký, không phải là hai hàm ẩn cho tên hằng số và thứ tự. Tôi không tin rằng đó là một lỗi trong javac mặc dù, nhưng nhiều hơn một thiếu trong spec VM. Các quy tắc của đặc tả VM cho thuộc tính chữ ký, cho khi nó được cho là có mặt và những gì nó được cho là chứa, dường như được xác định một cách mơ hồ. – Boann

+0

Một vấn đề tương tự là với các nhà xây dựng lớp bên trong, có một tham số ẩn được sử dụng để chuyển tham chiếu đến lớp bên ngoài. Ví dụ: 'class Outer {class Inner {Inner() {}}}' - hàm tạo xuất hiện trong phần tháo gỡ như: 'Outer $ Inner (Outer);'. Nhưng nếu constructor được tạo ra * generic *: 'class Outer {class Inner { Inner() {}}}', nó nhận được một thuộc tính signature trong classfile và javap tách nó thành ' Outer $ Inner();' - * tham số đột nhiên vô hình *, và điều này đúng với cả javac và ECJ. Thứ kỳ lạ! Ít nhất tùy chọn '-v' tiết lộ sự thật! – Boann

Các vấn đề liên quan