2015-09-20 18 views
5

Trong khi nguyên tắc chính của đa hình là tách "những gì từ ai" trong điều khoản của types, nhưng những gì gây nhầm lẫn cho tôi như thế nào cơ chế gọi phương thức phát hiện và gọi cơ thể phương pháp chính xác trong đa hình.Làm thế nào để Java Xác định các phương thức gọi lúc chạy trong đa hình?

Vì trong java tất cả các phương pháp ràng buộc là late-binding trừ khi phương pháp này là static, final hoặc private, và late-binding được thực hiện bởi JVM mà precomputes method table cho mỗi lớp và sau đó làm một bảng tra cứu trong thời gian chạy trong cuộc gọi phương pháp thông thường.

Nhưng điều tương tự cũng xảy ra trong quá trình đa hình. Ví dụ

Giả sử tôi đã một lớp Generic Cycle với một phương pháp ride()

class Cycle { 

    public void ride(){ 
     System.out.println("I'm Riding generic Cycle()"); 
    } 

} 

Và tôi có ba chuyên ngành Lớp BicycleTricycleUnicycle kéo dài Generic lớp Cycle và ghi đè phương pháp ride() của nó.

class Bicycle extends Cycle { 

    public void ride() { 
     System.out.println("I'm riding Bicycle"); 

    } 

} 

class Tricycle extends Cycle{ 

    public void ride() { 
     System.out.println("I'm riding Tricycle "); 

    } 

} 

class Unicycle extends Cycle { 

    public void ride() { 
     System.out.println("I'm Riding Unicycle "); 

    } 

} 

Đây là lớp TestRide để kiểm tra tính đa hình trên.

public class TestRide { 

    public static void ride(Cycle c){ 
     c.ride(); 
    } 

    public static void main(String[] args){ 

     Cycle Cycling = new Cycle(); 
     ride(Cycling); 

     Bicycle bi = new Bicycle(); 
     ride(bi); 

     Tricycle tri = new Tricycle(); 
     ride(tri); 

     Unicycle uni = new Unicycle(); 
     ride(uni); 
    } 

} 

Các Output là

I'm Riding generic Cycle() 
I'm riding Bicycle 
I'm riding Tricycle 
I'm Riding Unicycle 

Byte Code:

public static void main(java.lang.String[]); 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=2, locals=5, args_size=1 
     0: new   #17     // class com/polymorphism/Cycle 
     3: dup 
     4: invokespecial #24     // Method com/polymorphism/Cycle." 
<init>":()V 
     7: astore_1 
     8: aload_1 
     9: invokestatic #25     // Method ride:(Lcom/polymorphism/ 
Cycle;)V 
     12: new   #27     // class com/polymorphism/Bicycle 
     15: dup 
     16: invokespecial #29     // Method com/polymorphism/Bicycle 
."<init>":()V 
     19: astore_2 
     20: aload_2 
     21: invokestatic #25     // Method ride:(Lcom/polymorphism/ 
Cycle;)V 
     24: new   #30     // class com/polymorphism/Tricycle 

     27: dup 
     28: invokespecial #32     // Method com/polymorphism/Tricycl 
e."<init>":()V 
     31: astore_3 
     32: aload_3 
     33: invokestatic #25     // Method ride:(Lcom/polymorphism/ 
Cycle;)V 
     36: new   #33     // class com/polymorphism/Unicycle 

     39: dup 
     40: invokespecial #35     // Method com/polymorphism/Unicycl 
e."<init>":()V 
     43: astore  4 
     45: aload   4 
     47: invokestatic #25     // Method ride:(Lcom/polymorphism/ 
Cycle;)V 
     50: return 

Ngay cả trong bytecode của nó cũng giống như lời gọi phương thức thông thường với invokestaticinvokespecial trong khi tôi nghĩ rằng nó sẽ sử dụng invokedynamic với con số ra phiên bản của phương thức phù hợp với loại đối tượng thực tế. Nhưng đó không phải là trường hợp.

Vậy Java làm cách nào để tìm ra phương thức thực tế trong suốt quá trình đa hình trong khi chúng ta chỉ truyền một đối tượng upcasted trong phương thức ride() như ride(bi) trong lớp TestRide?

EDIT: RIDE phương pháp bytecode

public static void ride(com.polymorphism.Cycle); 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
     stack=1, locals=1, args_size=1 
     0: aload_0 
     1: invokevirtual #16     // Method com/polymorphism/Cycle.r 
ide:()V 
     4: return 
+1

Có thể trường hợp thử nghiệm của bạn quá đơn giản. Nếu trình biên dịch Java biết tại thời gian biên dịch, phương thức nào sẽ được gọi, không cần bytecode làm bất cứ điều gì động. – mastov

+1

'invokedynamic' được giới thiệu trong JVM bytecode để tra cứu phương thức động * không * theo tra cứu phương thức của Java. Tại sao một trình biên dịch Java sử dụng nó? Phương thức xử lý ảo được thực hiện với 'invokevirtual' và' invokeinterface'. Lưu ý rằng một trình biên dịch Java là tất nhiên miễn phí để sử dụng 'invokedynamic' nếu nó muốn. Đặc tả Ngôn ngữ Java không nói gì về cách Java được biên dịch, nó thậm chí không nói nó phải được biên dịch chút nào, nó cũng có thể được giải thích. –

+0

@ JörgWMittag Có điểm của bạn :). Nhưng câu hỏi về việc nó vẫn như thế nào vẫn còn –

Trả lời

0

Tôi nghĩ @JBNizet tìm ra các giải pháp đã có trong các ý kiến ​​(và tôi đoán hóa ra là sai). Nhưng vì anh không gửi nó như một câu trả lời, tôi sẽ làm điều đó:

Phương pháp main là không được phép hiển thị bất kỳ hành vi năng động bởi vì nó luôn luôn bao giờ sẽ gọi đó là một đơn phương pháp TestRide.ride(Cycle c). Không có phương pháp nào khác có thể, do đó không có gì để tìm ra.

Cuộc gọi phương thức động nằm bên trong phương thức đó TestRide.ride(Cycle c). Và bây giờ bạn đã đăng mã đó, thực sự chúng ta thấy một công văn động bằng cách sử dụng invokevirtual. Vì vậy, sau khi tất cả, không có bất ngờ. Phương thức động là có.

1

Trước hết là invokedynamic dành cho Java 8 lambdas và mã không phải Java, vì vậy bạn có thể quên điều đó.

Ngoài ra, có bốn hướng dẫn gọi (invokespecial, invokestatic, invokevirtualinvokeinterface). Bạn có thể thấy ngữ nghĩa chính xác trong phân tách JVM, nhưng dòng dưới cùng là các cuộc gọi invokevirtualinvokeinterfaceảo, tức là phương thức thực tế được gọi là thời gian chạy dựa trên loại conrete của đích.

Cuộc gọi ảo duy nhất trong mã của bạn nằm trong TestRide.ride. Mục tiêu được liệt kê là Cycle.ride:()V. Tuy nhiên, vì nó là một cuộc gọi ảo, JVM sẽ kiểm tra kiểu thực tế của đối số đầu tiên trong thời gian chạy và gọi phiên bản có nguồn gốc cao nhất của phương thức đó.

Điều này tương tự như các cuộc gọi phương thức ảo trong C++, ngoại trừ việc trừu tượng hóa trình biên dịch JVM và JIT cho phép khả năng triển khai tối ưu hơn.

Cũng lưu ý rằng điều này không bị nhầm lẫn với quá tải phương thức, đây là một dạng đa hình thời gian biên dịch. Đối với các phương thức quá tải, trình biên dịch chọn cái nào để gọi dựa trên kiểu thời gian biên dịch của các đối số.

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