2010-08-27 28 views
16
public class InterfaceCasting { 

    private static class A{} 

    public static void main(String[] args) { 
     A a = new A(); 
     Serializable serializable = new Serializable(){}; 
     a = (A)serializable; 
    } 

} 

Compilation thành công nhưng Runtime ngoại lệJava Đúc Interface Class

Exception in thread "main" java.lang.ClassCastException: InterfaceCasting$1 cannot be cast to InterfaceCasting$A 

TẠI SAO LẬP THÀNH CÔNG? Trình biên dịch phải biết rằng serialiazable không phải là A?

+0

tôi nghĩ bạn phải xem lại các khái niệm về ngoại lệ Java ... – ultrajohn

Trả lời

-1

Serializable không phải là A, vì vậy, nó ném ClassCastException.

+3

Điều đó đúng nhưng không phải là những gì OP hỏi. – cherouvim

0

Không thể biết rằng vì loại thời gian biên dịch là serializableSerializable.

Để minh họa, xem xét việc này:

private static class A{} 
private static class B implements Serializable {} 

Serializable serializable = new B(); 
A a = (A)serializable; 

này hoàn toàn giống với câu hỏi của bạn, nó biên dịch.

private static class A{} 
private static class B implements Serializable {} 

B b = new B(); 
A a = (A)b; 

điều này không biên dịch vì b không phải là A.

27

Như bạn chỉ ra, điều này sẽ biên dịch:

interface MyInterface {} 

class A {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     MyInterface myObject = new MyInterface() {}; 
     A a = (A) myObject; 
    } 
} 

này tuy nhiên, sẽ không biên dịch:

interface MyInterface {} 

class A {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     A a = (A) new MyInterface() {}; // javac says: "inconvertible types!" 
    } 
} 

Vì vậy, những gì đang xảy ra ở đây? Có gì khác biệt?

Vâng, kể từ MyInterface chỉ đơn giản là một giao diện, thể rất tốt được thực hiện bởi một lớp mà kéo dài Một, trong trường hợp các diễn viên MyInterface-A sẽ là hợp pháp.


Mã này ví dụ, sẽ thành công trong 50% của tất cả các hành, và minh họa rằng trình biên dịch sẽ cần phải giải quyết vấn đề có thể undecidable để luôn "phát hiện" diễn viên bất hợp pháp tại thời gian biên dịch.

interface MyInterface {} 

class A {} 

class B extends A implements MyInterface {} 

public class InterfaceCasting { 
    public static void main(String[] args) { 
     MyInterface myObject = new MyInterface() {}; 
     if (java.lang.Math.random() > 0.5) 
      myObject = new B(); 
     A a = (A) myObject; 
    } 
} 
+0

Điều này cũng có thể không mở rộng A? Bởi vì đó là những gì tôi cần. – Ben

4
Serializable serializable; 
a = (A)serializable; 

Đối với các trình biên dịch, các serializable biến có thể chứa bất kỳ đối tượng mà thực hiện Serializable, trong đó bao gồm các lớp con của A. Vì vậy, nó giả định rằng bạn biết rằng các biến thực sự chứa một đối tượng A và cho phép dòng đó.

+0

Không, nó có thể ** không ** bao gồm 'A'. Trình biên dịch biết chắc chắn rằng 'A' không thực hiện' Serializable'. – aioobe

+0

@aioobe có, nhưng một phân lớp của 'A' có thể –

+0

bạn nên làm rõ điều này bằng cách viết "bao gồm các lớp con của A". – aioobe

1

Trình biên dịch không đủ thông minh để theo dõi nguồn gốc của serializable và nhận ra rằng nó không bao giờ có thể thuộc loại A. Nó thực sự chỉ đánh giá dòng:

a = (A)serializable; 

và thấy rằng serializable một tài liệu tham khảo của loại Serializable nhưng nó có thể tham khảo một lớp cũng là loại A. Lớp thực tế mà tài liệu tham khảo serializable không được biết đến cho đến khi chạy.

Trong trường hợp tầm thường này, chúng ta biết rằng dàn diễn viên này sẽ không bao giờ thành công, nhưng nói chung điều này còn lại dưới dạng vấn đề thời gian chạy vì các đường dẫn mã khác nhau có thể dẫn đến việc đúc là (theo lý thuyết) vô hạn.

Nếu bạn muốn tránh vấn đề này tại thời gian chạy bạn có thể kiểm tra cho nó ..

if (serializable instanceof A) { 
    a = (A)serializable; 
} else .... 
0

Trong khi tôi không biết câu trả lời đúng, nó thường không phải là một ý tưởng tốt để đúc một giao diện để một lớp học, vì nhiều lý do.

a) Giao diện xác định hợp đồng, đảm bảo hành vi. Một lớp có thể xác định nhiều hơn hợp đồng này, việc sử dụng các phương thức khác có thể có các tác dụng phụ không mong muốn và phá vỡ các API. Ví dụ. khi một phương thức được chuyển qua một danh sách và bạn tìm ra đối tượng được truyền thực sự là một LinkedList và bạn cast nó và sử dụng các phương thức Queue dựa trên nó cũng định nghĩa, bạn đang phá vỡ API.

b) Ngoài ra, đối tượng có giao diện có thể không phải là đối tượng "thực" trong thời gian chạy, nhưng có lẽ một proxy dịch vụ được tạo xung quanh đối tượng gốc bằng thư viện như Spring hoặc EJB. Dàn diễn viên của bạn sẽ thất bại trong những trường hợp đó.

Nếu bắt buộc phải đúc, không bao giờ làm điều đó mà không có một kiểm tra instanceof:

if(myServiceObject instanceof MyServiceObjectImpl){ 
    MyServiceObjectImpl impl = (MyServiceObjectImpl) myServiceObject; 
} 
0

Các quy tắc chi tiết cho thời gian biên dịch hợp pháp của một chuyển đổi đúc của một giá trị của thời gian biên dịch kiểu tham chiếu S kiểu tham chiếu biên dịch T như sau:
[...]
Nếu S là kiểu giao diện:
- Nếu T là kiểu mảng, [...].
- Nếu T là một loại không phải là cuối cùng (§8.1.1), thì nếu có tồn tại siêu X của T và siêu Y của S, sao cho cả X và Y là các loại tham số được phân biệt rõ ràng và các dấu vết của X và Y là như nhau, một lỗi biên dịch xảy ra. Nếu không, dàn diễn viên luôn hợp pháp tại thời điểm biên dịch (bởi vì ngay cả khi T không thực hiện S, một phân lớp của T có thể).

Nguồn:
JLS : Conversions and Promotions

8

Java language specification tiểu bang, rằng:

Một số phôi có thể được chứng minh không chính xác tại thời gian biên dịch; các phôi như vậy dẫn đến lỗi biên dịch.

Và sau đó trên chương trình Các quy tắc chi tiết cho thời gian biên dịch hợp pháp của một chuyển đổi đúc của một giá trị của thời gian biên dịch kiểu tham chiếu S để một thời gian biên dịch kiểu tham chiếu T - hãy cẩn thận, họ rất phức tạp và khó hiểu.

Nguyên tắc thú vị là:

  • Nếu S là một giao diện loại:
    • Nếu T là một loại đó là không thức (§8.1.1) , nếu có tồn tại siêu X của T và siêu Y của S, sao cho cả X và Y là các loại tham số được phân biệt rõ ràng và việc xóa của X và Y giống nhau, một lỗi biên dịch o ccurs. Nếu không, dàn diễn viên luôn hợp pháp tại thời gian biên dịch (vì ngay cả khi T không thực hiện S, một phân lớp của T có thể).

Trong ví dụ của bạn, rõ ràng là dàn diễn viên là bất hợp pháp. Nhưng xem xét thay đổi nhỏ này:

public class InterfaceCasting { 

    private static class A{} 
    private static class B extends A implements Serializable{} 

    public static void main(String[] args) { 
     A a = new A(); 
     Serializable serializable = new B(){}; 
     a = (A)serializable; 
    }  
} 

Bây giờ một dàn diễn viên từ một Serializable để A có thể tại thời gian chạy và điều này cho thấy, rằng trong những trường hợp này, đó là trái tốt hơn để thời gian chạy để quyết định xem chúng ta có thể đúc hay không.

+0

vâng, tôi nhận ngay bây giờ Cảm ơn mọi người đã giúp đỡ của bạn – komenan