Pengikatan dinamis Java dan penggantian metode

90

Kemarin saya melakukan wawancara telepon teknis selama dua jam (yang saya lulus, woohoo!), Tetapi saya benar-benar meredam pertanyaan berikut tentang dynamic binding di Java. Dan ini sangat membingungkan karena saya dulu mengajarkan konsep ini kepada mahasiswa ketika saya masih menjadi TA beberapa tahun yang lalu, jadi kemungkinan bahwa saya memberi mereka informasi yang salah sedikit mengganggu ...

Inilah masalah yang saya berikan:

/* 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);
  }
}

Saya menegaskan bahwa output seharusnya berupa dua pernyataan cetak terpisah dari dalam equals()metode yang diganti : at t1.equals(t3)dan t3.equals(t3). Kasus terakhir cukup jelas, dan dengan kasus sebelumnya, meskipun t1memiliki referensi tipe Object, itu dibuat sebagai tipe Test, sehingga pengikatan dinamis harus memanggil bentuk metode yang diganti.

Sepertinya tidak. Pewawancara saya mendorong saya untuk menjalankan program sendiri, dan lihatlah, hanya ada satu keluaran dari metode yang diganti: di telepon t3.equals(t3).

Pertanyaan saya kemudian adalah, mengapa? Seperti yang sudah saya sebutkan, meskipun t1merupakan referensi dari tipe Objek (jadi pengikatan statis akan memanggil equals()metode Object ), pengikatan dinamis harus menjaga pemanggilan versi paling spesifik dari metode berdasarkan tipe referensi yang dipakai. Apa yang saya lewatkan?

Magsol
sumber
Mohon temukan posting saya untuk jawaban ini di mana saya telah mencoba yang terbaik untuk menjelaskan dengan kasus tambahan. Saya akan sangat menghargai masukan Anda :)
Devendra Lattu

Jawaban:

82

Java menggunakan pengikatan statis untuk metode yang kelebihan beban, dan pengikatan dinamis untuk metode yang diganti. Dalam contoh Anda, metode sama dengan kelebihan beban (memiliki tipe param yang berbeda dari Object.equals ()), sehingga metode yang dipanggil terikat ke tipe referensi pada waktu kompilasi.

Beberapa diskusi di sini

Fakta bahwa ini adalah metode yang sama tidak terlalu relevan, selain itu adalah kesalahan umum untuk membebani dan menimpanya, yang sudah Anda sadari berdasarkan jawaban Anda atas masalah dalam wawancara.

Edit: Deskripsi yang bagus juga di sini . Contoh ini menunjukkan masalah serupa yang terkait dengan jenis parameter, tetapi disebabkan oleh masalah yang sama.

Saya percaya jika pengikatan sebenarnya dinamis, maka setiap kasus di mana pemanggil dan parameternya adalah turunan dari Test akan menghasilkan metode yang diganti dipanggil. Jadi t3.equals (o1) akan menjadi satu-satunya case yang tidak dapat dicetak.

Robin
sumber
Banyak orang menunjukkan bahwa itu kelebihan beban dan tidak ditimpa, tetapi bahkan dengan itu Anda mengharapkannya untuk menyelesaikan yang kelebihan beban dengan benar. Sejauh ini, postingan Anda adalah satu-satunya yang menjawab pertanyaan dengan benar.
Bill K
4
Kesalahan saya benar-benar kehilangan fakta bahwa metode ini memang kelebihan beban daripada diganti. Saya melihat "sama dengan ()" dan langsung berpikir diwarisi-dan-diganti. Sepertinya saya, sekali lagi, mendapatkan konsep yang lebih luas dan lebih sulit dengan benar, tetapi mengacaukan detail sederhananya. : P
Magsol
14
alasan lain mengapa anotasi @Override ada.
Matt
1
Ulangi setelah saya: "Java menggunakan pengikatan statis untuk metode yang kelebihan beban, dan pengikatan dinamis untuk metode yang diganti" - +1
Mr_and_Mrs_D
1
jadi saya lulus tanpa mengetahui hal ini. Terima kasih!
Atieh
26

The equalsmetode Testtidak mengesampingkan equalsmetode java.lang.Object. Lihat tipe parameternya! The Testkelas overloading equalsdengan metode yang menerima Test.

Jika equalsmetode ini dimaksudkan untuk diganti, metode ini harus menggunakan anotasi @Override. Ini akan menyebabkan kesalahan kompilasi untuk menunjukkan kesalahan umum ini.

erickson
sumber
Ya, saya tidak begitu yakin mengapa saya melewatkan detail yang sederhana namun penting itu, tapi di situlah letak masalah saya. Terima kasih!
Magsol
1 karena menjadi jawaban yang benar untuk hasil penasaran penanya
matt b
Mohon temukan posting saya untuk jawaban ini di mana saya telah mencoba yang terbaik untuk menjelaskan dengan kasus tambahan. Saya akan sangat menghargai masukan Anda :)
Devendra Lattu
7

Yang cukup menarik, dalam kode Groovy (yang dapat dikompilasi ke file kelas), semua kecuali satu panggilan akan mengeksekusi pernyataan cetak. (Yang membandingkan Test dengan Object jelas tidak akan memanggil fungsi Test.equals (Test).) Ini karena groovy TIDAK melakukan pengetikan dinamis sepenuhnya. Ini sangat menarik karena tidak memiliki variabel yang diketik secara dinamis. Saya telah membaca di beberapa tempat bahwa ini dianggap berbahaya, karena programmer mengharapkan hal yang keren untuk java.

Benson
sumber
1
Sayangnya harga yang harus dibayar Groovy untuk itu adalah kinerja yang sangat besar, karena setiap pemanggilan metode menggunakan refleksi. Mengharapkan satu bahasa berfungsi persis sama dengan bahasa lain umumnya dianggap berbahaya. Seseorang perlu menyadari perbedaan.
Joachim Sauer
Harus bagus dan cepat dengan pemanggilan Dynamic di JDK7 (atau bahkan menggunakan teknik implementasi serupa saat ini).
Tom Hawtin - tackline
5

Java tidak mendukung co-variance dalam parameter, hanya dalam tipe kembalian.

Dengan kata lain, meskipun tipe kembalian Anda dalam metode penggantian mungkin merupakan subtipe dari apa yang telah ditimpa, itu tidak benar untuk parameter.

Jika parameter Anda untuk sama di Object adalah Object, menempatkan sama dengan apa pun di subclass akan menjadi metode overload, bukan override. Oleh karena itu, satu-satunya situasi di mana metode itu akan dipanggil adalah ketika tipe statis parameternya adalah Test, seperti dalam kasus T3.

Semoga berhasil dengan proses wawancara kerja! Saya ingin sekali diwawancarai di perusahaan yang menanyakan jenis pertanyaan ini daripada pertanyaan algo / struktur data biasa yang saya ajarkan kepada siswa saya.

Uri
sumber
1
Maksud Anda parameter kontravarian.
Tom Hawtin - tackline
Saya entah bagaimana benar-benar mengabaikan fakta bahwa parameter metode yang berbeda secara intrinsik membuat metode yang kelebihan beban, bukan yang ditimpa. Oh jangan khawatir, ada pertanyaan tentang algo / data structure juga. : P Dan terima kasih atas keberuntungannya, saya akan membutuhkannya! :)
Magsol
4

Saya pikir kuncinya terletak pada fakta bahwa metode equals () tidak sesuai dengan standar: Dibutuhkan di objek Uji lain, bukan objek Objek dan dengan demikian tidak menimpa metode equals (). Ini berarti Anda sebenarnya hanya membebani itu untuk melakukan sesuatu yang istimewa ketika diberikan objek Uji sambil memberinya objek Object memanggil Object.equals (Object o). Melihat kode itu melalui IDE apa pun akan menunjukkan dua metode equals () untuk Test.

P Arrayah
sumber
Ini, dan sebagian besar tanggapan kehilangan intinya. Masalahnya bukan tentang fakta bahwa kelebihan beban digunakan alih-alih menimpa. Itulah mengapa metode kelebihan beban tidak digunakan untuk t1.equals (t3), ketika t1 dideklarasikan sebagai Object tetapi diinisialisasi ke Test.
Robin
4

Metode ini kelebihan beban, bukan diganti. Sama selalu mengambil Objek sebagai parameter.

btw, Anda memiliki item tentang ini di Java efektif Bloch (yang harus Anda miliki).

Gilles
sumber
Java Efektif Joshua Bloch?
DJClayworth
Ya efektif, sedang memikirkan hal lain saat mengetik: D
Gilles
4

Beberapa catatan di Dynamic Binding (DD) dan Static Binding̣̣̣ (SB) setelah pencarian beberapa saat:

1. Waktu eksekusi : (Ref.1)

  • DB: saat dijalankan
  • SB: waktu kompilator

2. Digunakan untuk :

  • DB: menimpa
  • SB: overloading (statis, privat, final) (Ref.2)

Referensi:

  1. Jalankan resolver berarti metode mana yang lebih suka digunakan
  2. Karena tidak bisa mengganti metode dengan pengubah statis, pribadi atau final
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html
NguyenDat
sumber
2

Jika metode lain ditambahkan yang menggantikan alih-alih membebani, itu akan menjelaskan panggilan pengikatan dinamis pada waktu proses.

/ * Apa output dari program berikut? * /

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);
    }
}
Prabu R
sumber
0

Jawaban atas pertanyaan "mengapa?" begitulah definisi bahasa Java.

Mengutip artikel Wikipedia tentang Kovarian dan Kontravarian :

Kovarian tipe pengembalian diimplementasikan dalam bahasa pemrograman Java versi J2SE 5.0. Jenis parameter harus persis sama (invariant) untuk penggantian metode, jika tidak, metode akan kelebihan beban dengan definisi paralel.

Bahasa lain berbeda.

ykaganovich
sumber
Masalah saya kira-kira setara dengan melihat 3 + 3 dan menulis 9, lalu melihat 1 + 1 dan menulis 2. Saya mengerti bagaimana bahasa Java didefinisikan; dalam hal ini, untuk alasan apa pun, saya sepenuhnya salah mengira metode tersebut sebagai sesuatu yang bukan, meskipun saya menghindari kesalahan itu di tempat lain dalam masalah yang sama.
Magsol
0

Sangat jelas, bahwa tidak ada konsep menimpa di sini. Ini adalah metode overloading. yang Object()metode kelas Object mengambil parameter acuan dari jenis Obyek dan ini equal()metode mengambil parameter acuan tipe Test.

ankush gatfane
sumber
-1

Saya akan mencoba menjelaskan ini melalui dua contoh yang merupakan versi tambahan dari beberapa contoh yang saya temukan secara online.

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);
    }
}

Di sini, untuk baris dengan nilai hitungan 0, 1, 2, dan 3; kita memiliki referensi dari Object untuk o1 dan t1 pada equals()metode. Jadi, pada waktu kompilasi, equals()metode dari file Object.class akan dibatasi.

Namun, meskipun referensi dari t1 adalah Object , ia memiliki intialization dari class Test .
Object t1 = new Test();.
Oleh karena itu, pada saat run-time itu memanggil public boolean equals(Object other)yang merupakan

metode yang diganti

. masukkan deskripsi gambar di sini

Sekarang, untuk menghitung nilai 4 dan 6, sangat mudah bahwa t3 yang memiliki referensi dan inisialisasi Test memanggil equals()metode dengan parameter sebagai referensi Objek dan merupakan

metode kelebihan beban

BAIK!

Sekali lagi, untuk lebih memahami metode apa yang akan dipanggil oleh kompilator, cukup klik metode tersebut dan Eclipse akan menyorot metode jenis serupa yang menurutnya akan dipanggil pada waktu kompilasi. Jika tidak dipanggil pada waktu kompilasi maka metode tersebut adalah contoh metode penggantian.

masukkan deskripsi gambar di sini

Devendra Lattu
sumber