Liên kết động Java và ghi đè phương thức


89

Hôm qua, tôi đã có một cuộc phỏng vấn kỹ thuật qua điện thoại kéo dài hai giờ (mà tôi đã vượt qua, thật tuyệt!), Nhưng tôi đã hoàn thành câu hỏi sau về liên kết động trong Java. Và thật là khó hiểu vì tôi đã từng dạy khái niệm này cho sinh viên chưa tốt nghiệp khi tôi là một kỹ sư cách đây vài năm, vì vậy viễn cảnh mà tôi cung cấp cho họ thông tin sai lệch là một chút đáng lo ngại ...

Đây là vấn đề tôi đã được đưa ra:

/* What is the output of the following program? */

public class Test {

  public boolean equals( Test other ) {
    System.out.println( "Inside of Test.equals" );
    return false;
  }

  public static void main( String [] args ) {
    Object t1 = new Test();
    Object t2 = new Test();
    Test t3 = new Test();
    Object o1 = new Object();

    int count = 0;
    System.out.println( count++ );// prints 0
    t1.equals( t2 ) ;
    System.out.println( count++ );// prints 1
    t1.equals( t3 );
    System.out.println( count++ );// prints 2
    t3.equals( o1 );
    System.out.println( count++ );// prints 3
    t3.equals(t3);
    System.out.println( count++ );// prints 4
    t3.equals(t2);
  }
}

Tôi khẳng định rằng đầu ra phải là hai câu lệnh in riêng biệt từ bên trong equals()phương thức được ghi đè : at t1.equals(t3)t3.equals(t3). Trường hợp sau là đủ rõ ràng, và với trường hợp trước, mặc dù t1có tham chiếu kiểu Đối tượng, nó được khởi tạo dưới dạng Kiểm tra kiểu, vì vậy liên kết động nên gọi dạng ghi đè của phương thức.

Rõ ràng là không. Người phỏng vấn của tôi đã khuyến khích tôi tự chạy chương trình, và này, chỉ có một đầu ra duy nhất từ ​​phương thức bị ghi đè: tại dòng t3.equals(t3).

Câu hỏi của tôi sau đó là, tại sao? Như tôi đã đề cập, mặc dù t1là một tham chiếu của kiểu Đối tượng (vì vậy liên kết tĩnh sẽ gọi equals()phương thức của Đối tượng ), liên kết động nên quan tâm đến việc gọi phiên bản cụ thể nhất của phương thức dựa trên kiểu khởi tạo của tham chiếu. Tôi đang thiếu gì?


Vui lòng tìm bài đăng của tôi cho câu trả lời này, nơi tôi đã cố gắng hết sức để giải thích với các trường hợp bổ sung. Tôi thực sự đánh giá cao ý kiến ​​đóng góp của bạn :)
Devendra Lattu

Câu trả lời:


81

Java sử dụng liên kết tĩnh cho các phương thức được nạp chồng và liên kết động cho các phương thức bị ghi đè. Trong ví dụ của bạn, phương thức bằng được nạp chồng (có kiểu tham số khác với Object.equals ()), vì vậy phương thức được gọi được liên kết với kiểu tham chiếu tại thời điểm biên dịch.

Một số thảo luận ở đây

Thực tế là phương pháp bằng không thực sự phù hợp, ngoại trừ việc ghi đè thay vì ghi đè nó là một lỗi phổ biến mà bạn đã biết dựa trên câu trả lời của mình cho vấn đề trong cuộc phỏng vấn.

Chỉnh sửa: Một mô tả tốt ở đây là tốt. Thay vào đó, ví dụ này đang hiển thị một vấn đề tương tự liên quan đến loại tham số nhưng do cùng một vấn đề gây ra.

Tôi tin rằng nếu ràng buộc thực sự là động, thì bất kỳ trường hợp nào mà người gọi và tham số là một thể hiện của Kiểm tra sẽ dẫn đến phương thức được ghi đè được gọi. Vì vậy, t3.equals (o1) sẽ là trường hợp duy nhất không in.


Nhiều người chỉ ra rằng nó quá tải và không bị ghi đè, nhưng ngay cả với điều đó, bạn cũng mong đợi nó giải quyết chính xác tình trạng quá tải. Bài đăng của bạn thực sự là bài duy nhất cho đến nay trả lời câu hỏi một cách chính xác theo như tôi có thể nói.
Bill K

4
Sai lầm của tôi là hoàn toàn thiếu thực tế rằng phương thức thực sự là quá tải chứ không phải bị ghi đè. Tôi nhìn thấy "bằng ()" và ngay lập tức nghĩ rằng được kế thừa-và-ghi đè. Có vẻ như tôi, một lần nữa, đã hiểu đúng khái niệm rộng hơn và khó hơn, nhưng đã vặn chặt các chi tiết đơn giản. : P
Magsol

14
một lý do khác khiến chú thích @Override tồn tại.
Matt,

1
Lặp lại sau khi tôi: "Java sử dụng liên kết tĩnh cho các phương thức bị quá tải và liên kết động cho các phương thức bị ghi đè" - +1
Mr_and_Mrs_D

1
vì vậy tôi đã tốt nghiệp không biết điều này. Cảm ơn!
Atieh

25

Các equalsphương pháp Testkhông ghi đè lên các equalsphương pháp java.lang.Object. Nhìn vào loại tham số! Các Testlớp được quá tải equalsvới một phương pháp mà chấp nhận một Test.

Nếu equalsphương thức được dự định ghi đè, thì phương thức đó phải sử dụng chú thích @Override. Điều này sẽ gây ra lỗi biên dịch để chỉ ra lỗi phổ biến này.


Vâng, tôi không chắc tại sao tôi lại bỏ lỡ chi tiết đơn giản nhưng quan trọng đó, nhưng đó chính xác là vấn đề của tôi. Cảm ơn bạn!
Magsol

+1 vì trở thành câu trả lời thực sự cho kết quả tò mò của người hỏi
matt b

Vui lòng tìm bài đăng của tôi cho câu trả lời này, nơi tôi đã cố gắng hết sức để giải thích với các trường hợp bổ sung. Tôi thực sự đánh giá cao ý kiến ​​đóng góp của bạn :)
Devendra Lattu

6

Điều thú vị là trong mã Groovy (có thể được biên dịch thành tệp lớp), tất cả trừ một trong các lệnh gọi sẽ thực hiện câu lệnh in. (Chức năng so sánh Kiểm tra với Đối tượng rõ ràng sẽ không gọi hàm Test.equals (Kiểm tra).) Điều này là do việc nhập liệu hoàn toàn động. Điều này đặc biệt được quan tâm vì nó không có bất kỳ biến nào được nhập động một cách rõ ràng. Tôi đã đọc ở một số nơi rằng điều này được coi là có hại, vì các lập trình viên mong đợi groovy thực hiện điều java.


1
Thật không may, cái giá mà Groovy trả cho đó là một cú đánh hiệu suất lớn, vì mọi lệnh gọi phương thức đều sử dụng phản xạ. Mong đợi một ngôn ngữ hoạt động hoàn toàn giống với ngôn ngữ khác thường bị coi là có hại. Người ta cần nhận thức được sự khác biệt.
Joachim Sauer

Sẽ tốt đẹp và nhanh chóng với invokedynamic trong JDK7 (hoặc thậm chí sử dụng kỹ thuật triển khai tương tự ngày nay).
Tom Hawtin - tackline

5

Java không hỗ trợ đồng phương sai trong các tham số, chỉ trong các kiểu trả về.

Nói cách khác, trong khi kiểu trả về của bạn trong một phương thức ghi đè có thể là một kiểu con của kiểu bị ghi đè, nhưng điều đó không đúng với các tham số.

Nếu tham số của bạn cho bằng trong Đối tượng là Đối tượng, thì việc đặt bằng với bất kỳ thứ gì khác trong lớp con sẽ là một phương thức được nạp chồng, không phải là phương thức bị ghi đè. Do đó, tình huống duy nhất mà phương thức đó sẽ được gọi là khi kiểu tĩnh của tham số là Kiểm tra, như trong trường hợp của T3.

Chúc may mắn với quá trình phỏng vấn xin việc! Tôi rất muốn được phỏng vấn tại một công ty hỏi những loại câu hỏi này thay vì những câu hỏi về cấu trúc dữ liệu / bí danh thông thường mà tôi dạy cho sinh viên của mình.


1
Bạn có nghĩa là các tham số trái ngược.
Tom Hawtin - tackline

Bằng cách nào đó, tôi hoàn toàn phủ nhận thực tế là các tham số phương thức khác nhau về bản chất tạo ra một phương thức được nạp chồng, không phải là một phương thức bị ghi đè. Đừng lo lắng, cũng có những câu hỏi về cấu trúc dữ liệu / thuật toán. : P Và cảm ơn vì sự may mắn, tôi sẽ cần nó! :)
Magsol

4

Tôi nghĩ rằng chìa khóa nằm ở thực tế là phương thức equals () không tuân theo tiêu chuẩn: Nó sử dụng một đối tượng Test khác, không phải đối tượng Object và do đó không ghi đè phương thức equals (). Điều này có nghĩa là bạn thực sự chỉ nạp chồng nó để làm điều gì đó đặc biệt khi nó được cung cấp đối tượng Kiểm tra trong khi cung cấp cho nó Đối tượng đối tượng gọi Object.equals (Đối tượng o). Nhìn mã đó thông qua bất kỳ IDE nào sẽ cho bạn thấy hai phương thức bằng () để Kiểm tra.


Điều này và hầu hết các câu trả lời đều thiếu điểm. Vấn đề không phải là thực tế là quá tải đang được sử dụng thay vì ghi đè. Đó là lý do tại sao phương thức nạp chồng không được sử dụng cho t1.equals (t3), khi t1 được khai báo là Đối tượng nhưng được khởi tạo để Kiểm tra.
Robin

4

Phương thức được ghi đè thay vì ghi đè. Các dấu bằng luôn nhận một Đối tượng làm tham số.

btw, bạn có một mục về điều này trong java hiệu quả của Bloch (mà bạn nên sở hữu).


Joshua Bloch's Java hiệu quả?
DJClayworth

Có hiệu quả, vâng, đang nghĩ về điều gì đó khác trong khi nhập: D
Gilles

4

Một số lưu ý trong Liên kết động (DD) và Liên kết tĩnḥ̣̣ (SB) sau một thời gian tìm kiếm:

1. thời gian thực hiện : (Tham khảo 1)

  • DB: lúc chạy
  • SB: thời gian biên dịch

2. sử dụng cho :

  • DB: ghi đè
  • SB: quá tải (tĩnh, riêng tư, cuối cùng) (Tham khảo 2)

Tài liệu tham khảo:

  1. Thực thi trình giải quyết nghĩa là phương pháp thích sử dụng
  2. Bởi vì không thể ghi đè phương thức với sửa đổi tĩnh, riêng tư hoặc cuối cùng
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

2

Nếu một phương thức khác được thêm vào sẽ ghi đè thay vì quá tải, nó sẽ giải thích lời gọi ràng buộc động tại thời điểm chạy.

/ * Kết quả của chương trình sau là gì? * /

public class DynamicBinding {
    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside @override: this is dynamic binding");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++);// prints 0
        t1.equals(t2);
        System.out.println(count++);// prints 1
        t1.equals(t3);
        System.out.println(count++);// prints 2
        t3.equals(o1);
        System.out.println(count++);// prints 3
        t3.equals(t3);
        System.out.println(count++);// prints 4
        t3.equals(t2);
    }
}


0

Câu trả lời cho câu hỏi "tại sao?" đó là cách ngôn ngữ Java được định nghĩa.

Để trích dẫn bài viết trên Wikipedia về Phương sai và Tương phản :

Hiệp phương sai kiểu trả về được thực hiện trong phiên bản ngôn ngữ lập trình Java J2SE 5.0. Các kiểu tham số phải hoàn toàn giống nhau (bất biến) để ghi đè phương thức, nếu không phương thức được nạp chồng bằng một định nghĩa song song.

Các ngôn ngữ khác là khác nhau.


Vấn đề của tôi gần tương đương với việc xem 3 + 3 và viết 9, sau đó thấy 1 + 1 và viết 2. Tôi hiểu cách ngôn ngữ Java được định nghĩa; trong trường hợp này, vì bất cứ lý do gì, tôi hoàn toàn nhầm lẫn phương pháp với một thứ không phải, mặc dù tôi đã tránh được sai lầm đó ở những nơi khác trong cùng một vấn đề.
Magsol

0

Rất rõ ràng, không có khái niệm ghi đè ở đây. Nó là quá tải phương thức. các Object()phương pháp của lớp Object có tham số tham chiếu của kiểu Object và điều này equal()phương pháp có tham số tham chiếu của kiểu Test.


-1

Tôi sẽ cố gắng giải thích điều này thông qua hai ví dụ là phiên bản mở rộng của một số ví dụ mà tôi đã xem trên mạng.

public class Test {

    public boolean equals(Test other) {
        System.out.println("Inside of Test.equals");
        return false;
    }

    @Override
    public boolean equals(Object other) {
        System.out.println("Inside of Test.equals ot type Object");
        return false;
    }

    public static void main(String[] args) {
        Object t1 = new Test();
        Object t2 = new Test();
        Test t3 = new Test();
        Object o1 = new Object();

        int count = 0;
        System.out.println(count++); // prints 0
        o1.equals(t2);

        System.out.println("\n" + count++); // prints 1
        o1.equals(t3);

        System.out.println("\n" + count++);// prints 2
        t1.equals(t2);

        System.out.println("\n" + count++);// prints 3
        t1.equals(t3);

        System.out.println("\n" + count++);// prints 4
        t3.equals(o1);

        System.out.println("\n" + count++);// prints 5
        t3.equals(t3);

        System.out.println("\n" + count++);// prints 6
        t3.equals(t2);
    }
}

Ở đây, đối với các dòng có giá trị đếm 0, 1, 2 và 3; chúng tôi có tham khảo của Object cho o1t1 trên equals()phương pháp. Do đó, tại thời điểm biên dịch, equals()phương thức từ tệp Object.class sẽ bị ràng buộc.

Tuy nhiên, mặc dù tài liệu tham khảo của t1đối tượng , nó có intialization của class Test .
Object t1 = new Test();.
Do đó, tại thời điểm chạy, nó gọi public boolean equals(Object other)

phương pháp ghi đè

. nhập mô tả hình ảnh ở đây

Bây giờ, đối với các giá trị đếm là 4 và 6, một lần nữa đơn giản rằng t3tham chiếukhởi tạo Kiểm tra đang gọi equals()phương thức với tham số là tham chiếu Đối tượng và là một

phương pháp quá tải

ĐỒNG Ý!

Một lần nữa, để hiểu rõ hơn về phương thức mà trình biên dịch sẽ gọi, chỉ cần nhấp vào phương thức và Eclipse sẽ đánh dấu các phương thức của các kiểu tương tự mà nó cho là sẽ gọi tại thời điểm biên dịch. Nếu nó không được gọi vào lúc biên dịch thì các phương thức đó là một ví dụ về ghi đè phương thức.

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.