2011-10-28 27 views
6

Trong dự án hiện tại của tôi, tôi có các lớp được mô hình hóa như sau. Tại một số thời điểm, một phương thức như getReturnTypeForGetId() được gọi trên các lớp AB. Gọi phương thức với số A trả về Integer như mong đợi, nhưng B trả về Serializable.Thừa kế chung và gọi GetMethod(). GetReturnType()

Tôi thiếu gì ở đây? Tôi có bị cắn bởi một số điều tẩy xoá ghê tởm, hay tôi chỉ bỏ lỡ một số loại bối cảnh bối cảnh chung?

EDIT: Thêm một trên một thanh cuộn getId() phương pháp để B sửa chữa vấn đề này, nhưng tôi sẽ vẫn muốn hiểu những gì tôi đang chạy vào.

import java.io.Serializable; 

public class WeirdTester { 
    static interface Identifiable<T extends Serializable> { 
     T getId(); 
     void setId(final T id); 
    } 

    static abstract class BaseEntity<T extends Serializable> implements Identifiable<T> { 
     private T id; 
     public T getId() { return id; } 
     public void setId(final T id) { this.id = id; } 
    } 

    static class A implements Identifiable<Integer> { 
     private Integer id; 
     public Integer getId() { return id; } 
     public void setId(final Integer id) { this.id = id; } 
    } 

    static class B extends BaseEntity<Integer> {} 

    @SuppressWarnings("unchecked") 
    private static <T extends Serializable, Q extends Identifiable<T>> Class<T> getReturnTypeForGetId(
      final Class<Q> clazz) throws Exception { 
     return (Class<T>) clazz.getMethod("getId", (Class[])null).getReturnType(); 
    } 

    public static void main(final String[] args) throws Exception { 
     System.out.println(getReturnTypeForGetId(A.class)); 
     // CONSOLE: "class java.lang.Integer" 
     System.out.println(getReturnTypeForGetId(B.class)); 
     // CONSOLE: "interface java.io.Serializable" 
    } 
} 
+0

Sẽ không dễ dàng hơn khi đạt được những gì bạn đang cố gắng sử dụng với các thẻ siêu loại? http://gafter.blogspot.com/2006/12/super-type-tokens.html – millimoose

Trả lời

2

Có nhiều phương thức getId trong lớp biên dịch A. Bạn nhận được một phương thức cầu cho kiểu trả về biến đổi (một "tiểu thuyết" của ngôn ngữ không được phản ánh trong máy ảo). Các đặc điểm kỹ thuật cho Class.getMethod nói rằng nó sẽ trả về phương thức với kiểu trả về cụ thể nhất (giả định rằng tồn tại). Nó thực hiện điều này cho A, nhưng đối với B, phương thức này không được ghi đè để javac tránh tổng hợp một phương thức cầu không cần thiết.

Thực tế, đối với ví dụ này, tất cả thông tin vẫn có trong tệp lớp. (Trước đó tôi nói nó không phải là erased. Điều đó không đúng, nhưng tẩy xoá không có nghĩa là nó không có ở đó!) Thông tin chung tuy nhiên một chút khó khăn để trích xuất (nó sẽ ở trong Identifiable.class.getGenericReturnType(), Identifiable.class.getTypeParameters(), BaseEntity.class.getGenericInterfaces, BaseEntity.class.getTypeParameters()B.getGenericSuperclass (Tôi nghĩ vậy!)).

Sử dụng javap để xem chính xác những gì bạn có trong tệp lớp học.

+0

Tôi không chắc chắn nếu tôi hiểu câu trả lời của bạn. Đối với tôi, nếu bạn thêm setId2 (id tuần tự cuối) vào BaseEntity, trường id có thể chứa bất kỳ Serializable không chỉ là Integer, do đó nếu getId() không bị ghi đè trong B (và do đó bắt buộc phải trả về Integer) bất kỳ Serializable nào đi ra khỏi nó. Kiểm tra xem không có phương thức nào có thể đặt trường id thành bất kỳ thứ gì khác ngoài Integer không phải là công việc của trình biên dịch/JVM. – MarianP

+0

Để thực thi 'setId2' sao cho nó thực sự hoạt động,' this.id = (T) id; ', sẽ đưa ra một cảnh báo (nên được chú ý !!). 'getId' trên một thể hiện của' B' sau đó sẽ ném một 'ClassCastException' từ người gọi (tôi nghĩ). –

+0

(Tôi nghĩ ... không chính xác.) Một lỗi đã được sửa chữa sớm là javac sẽ truyền đến quá tải chính xác. Vì vậy, nếu 'T' là' char [] 'mà sẽ CCE. –

2

Trong lớp A, bạn ghi đè getId để trả về Số nguyên.

Trong lớp B bạn không ghi đè getId, vì vậy phương thức getId trong B là phương thức từ BaseEntity. Vì xóa, cái đó trả về Serializable.

+0

sai. Điều này không liên quan gì đến việc xóa bỏ. BaseEntity.id có thể chứa bất kỳ Serializable nào. (suy luận của rằng không có phương pháp nào đặt nó cho bất kỳ Serializable khác là cách trên trình biên dịch nào). Nếu B không ghi đè getId(), nơi nó sẽ được thi hành getId() chỉ trả về Integer, bất kỳ Serializable nào có thể xuất phát từ getId() – MarianP

+0

@MarianP: Tôi nghĩ bạn có thể muốn đọc lại về "loại tẩy xóa" có nghĩa là, vì nó * không * xảy ra trong trường hợp đã đề cập. – JimmyB

+0

@MarianP Bạn nói "sai", nhưng phần còn lại của nhận xét của bạn là đầu cơ. Bạn đã thực sự thử giải mã để xem javac có làm gì không? Tôi có. Việc thực thi các generics được thực hiện thông qua một sự kết hợp các chứng minh tĩnh (chỉ có giá trị khi không có các phôi không được kiểm soát) và các phôi thời gian chạy. –

1

Câu trả lời thực sự là loại xóa. Hãy nhớ rằng generics chỉ là một thủ thuật, gợi ý trong mã Java không biên dịch. Trình biên dịch loại bỏ mọi thứ liên quan đến chúng để tạo ra bytecode. Vì vậy, khi bạn sử dụng sự phản chiếu trên phương thức getId, bạn chỉ nhận được kiểu thô.

http://download.oracle.com/javase/tutorial/java/generics/erasure.html

Nhưng nếu bạn hỏi cho lớp của một đối tượng thực tế được trả về bởi phương pháp này (B.getId), mà không sử dụng phản chiếu, do cách nó được xây dựng, bạn sẽ nhận được một Integer.

+0

của tôi. BaseEntity.id có thể chứa bất kỳ Serializable nào. (suy luận của rằng không có phương pháp nào đặt nó cho bất kỳ Serializable khác là cách trên trình biên dịch nào). Nếu B không ghi đè getId(), nó sẽ được thi hành ở đâu getId() chỉ trả về Integer, bất kỳ Serializable nào có thể xuất phát từ getId() – MarianP

-1

Java cho phép cái gọi là "thu hẹp" các loại giá trị trả về. Đó là lý do ví dụ của bạn hoạt động ở tất cả:

Serializable getId()

thể được ghi đè với bất kỳ kiểu trả serializable, như

Integer getId(), như Integer cụ Serializable, do đó thu hẹp được cho phép trong trường hợp này.

B không không override getId()getId() là giống như một thừa hưởng từ BaseEntity. Việc kê khai

class B extends BaseEntity<Integer> 

là "type-xóa" tại thời gian biên dịch để

class B extends BaseEntity 

và, thì đấy, chúng tôi nhận được kết quả quan sát.

+0

nghĩa là trả về biến đổi? nó chỉ nói rằng bạn có thể sử dụng một kiểu con của kiểu trả về trong phương thức ghi đè. Nó không liên quan đến câu hỏi này. – MarianP

+0

Xin lỗi, nhưng bạn đã không nhận được điểm: Đối với lớp chung (và vì vậy cho * B *) ở trên, * T getId() * sẽ được biên dịch thành * Serializable getId() *. Đó là lý do tại sao bằng cách phản ánh kiểu trả về * Serializable * được trả về. Trong lớp * A * * Số nguyên getId() * ghi đè quyền thừa kế * Serializable getId() * vì * Số nguyên * được tuần tự hóa. Vì vậy, khi biên soạn, lớp * A * của phương pháp sẽ trả về một * Integer *, mà chỉ là những gì phản ánh cho bạn biết tại thời gian chạy, như OP quan sát thấy. – JimmyB

+0

điều này là đúng. bạn thực sự không lý do tất cả chiều dài này trong câu trả lời của bạn. Chỉnh sửa một số char trong câu trả lời của bạn xin vui lòng để tôi có thể hủy bỏ downvote của tôi. – MarianP

1

id trong BaseEntity là tin và 'Serializable hoặc mở rộng Serializable'.

Lớp B (mở rộng BaseEntity) không biết gì về trường này. Nếu nó được định nghĩa riêng của mình id và không ghi đè getId()/setId (...) hai phương pháp sẽ tiếp tục sử dụng BaseEntity.id

Nếu bạn thêm phương pháp này trong BaseEntity:

public void setId2(final Serializable id) { 
     this.id = (T) id; 
} 

nó cho phép bạn đặt BaseEntity.id thành bất kỳ Serializable nào.

Trong thử nghiệm sau, bạn có thể đặt trường id thành ví dụ: a Float giá trị và mọi thứ biên dịch và không thay đổi getId() thoải mái trả về giá trị Float.

B b = new B(); 
b.setId2(2.1F); 
System.out.println(b.getId()); //prints out 2.1 

Do đó, nếu bạn làm những gì bạn làm và yêu cầu 'kiểu trả về của B.getId() phương pháp là gì, sau đó trừ khi bạn ghi đè phương thức trong lớp B getId() (trong đó sẽ buộc nó để sử dụng kiểu hàm Integer và trả về Integer cho chắc chắn. Lưu ý rằng BaseEntity.id sẽ không được hiển thị cho B sau đó!) Câu trả lời của phản chiếu không phải là Integer mà là một Serializable chung. Bởi vì bất kỳ Serializable nào có thể xuất phát từ phương thức getId() thực sự.

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