Java dinamik bağlama ve yöntemi geçersiz kılma


90

Dün iki saatlik bir teknik telefon görüşmesi yaptım (geçtim, woohoo!), Ancak Java'da dinamik bağlama ile ilgili aşağıdaki soruyu tamamen boğdum. Ve bu iki kez kafa karıştırıcı çünkü birkaç yıl önce asistanlık yaparken bu kavramı lisans öğrencilerine öğretiyordum, bu yüzden onlara yanlış bilgi verme ihtimalim biraz rahatsız edici ...

İşte bana verilen sorun:

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

Çıktının geçersiz kılınan equals()yöntem içinden iki ayrı baskı ifadesi olması gerektiğini iddia ettim : at t1.equals(t3)ve t3.equals(t3). İkinci durum yeterince açıktır ve ilk durumda, t1Object türünde bir referansa sahip olmasına rağmen , Tip Test olarak somutlaştırılmıştır, bu nedenle dinamik bağlama, yöntemin geçersiz kılınan biçimini çağırmalıdır.

Görünüşe göre öyle değil. Görüşmeci, programı kendim yürütmem için beni cesaretlendirdi ve bakın, geçersiz kılınan yöntemden yalnızca tek bir çıktı vardı: satırda t3.equals(t3).

O zaman sorum şu, neden? Daha önce bahsettiğim gibi, t1Object türünün bir referansı olsa da (bu nedenle statik bağlama, Object'in equals()yöntemini çağırır ), dinamik bağlama , referansın somutlaştırılmış türüne dayalı olarak yöntemin en spesifik sürümünü çağırmaya dikkat etmelidir . Neyi kaçırıyorum?


Ek vakalarla açıklamak için elimden gelenin en iyisini yaptığım bu yanıta lütfen gönderimi bulun. Girişlerinizi gerçekten takdir ediyorum :)
Devendra Lattu

Yanıtlar:


82

Java, aşırı yüklenmiş yöntemler için statik bağlama ve geçersiz kılınmış yöntemler için dinamik bağlama kullanır. Örneğinizde, eşittir yöntemi aşırı yüklenmiştir (Object.equals () 'den farklı bir param türüne sahiptir), bu nedenle çağrılan yöntem referansa bağlıdır , derleme zamanında türüne .

Burada biraz tartışma

Eşittir yöntemi olduğu gerçeği, röportajda soruna verdiğiniz cevaba dayanarak zaten farkında olduğunuz, onu geçersiz kılmak yerine aşırı yüklemenin yaygın bir hata olması dışında, gerçekten alakalı değildir.

Düzenleme: Burada da iyi bir açıklama . Bu örnek, bunun yerine parametre türü ile ilgili benzer bir sorunu göstermektedir, ancak aynı sorundan kaynaklanmaktadır.

Bağlama gerçekten dinamik olsaydı, arayanın ve parametrenin Test'in bir örneği olduğu her durumda geçersiz kılınan yöntemin çağrılmasına neden olacağına inanıyorum. Yani t3.equals (o1) basılmayan tek durum olacaktır.


Pek çok insan aşırı yüklendiğine ve geçersiz kılınmadığına dikkat çekiyor, ancak bununla bile aşırı yüklenmiş olanı doğru bir şekilde çözmesini beklersiniz. Gönderiniz aslında soruyu anlayabildiğim kadarıyla şu ana kadar doğru cevaplayan tek mesajdır.
Bill K

4
Benim hatam, yöntemin gerçekten geçersiz kılınmak yerine aşırı yüklenmiş olduğu gerçeğini tamamen kaçırmaktı. "Eşittir ()" i gördüm ve hemen miras alındığını ve geçersiz kılındığını düşündüm. Görünüşe göre, daha geniş ve daha zor konsepti doğru buldum, ancak basit ayrıntıları mahvettim. : P
Magsol

14
@Override açıklamasının var olmasının başka bir nedeni.
Matt

1
Benden sonra tekrar edin: "Java, aşırı yüklenmiş yöntemler için statik bağlama ve geçersiz
kılınanlar

1
bu yüzden bunu bilmeden mezun oldum. Teşekkürler!
Atieh

26

equalsYöntem Testgeçersiz kılmaz equalsyöntemini java.lang.Object. Parametre tipine bakın! TestSınıf aşırı olan equalsa kabul eden bir yöntem ileTest .

Eğer equalsyöntem geçersiz kılmak için tasarlanmıştır, bu @Override ek açıklama kullanmalıdır. Bu, bir derleme hatasının bu yaygın hataya işaret etmesine neden olur.


Evet, bu kadar basit ama önemli detayı neden kaçırdığımdan tam olarak emin değilim, ama benim sorunum tam olarak buydu. Teşekkür ederim!
Magsol

Soruyu soran kişinin merak ettiği sonuçlara gerçek yanıt için +1
matt b

Ek vakalarla açıklamak için elimden gelenin en iyisini yaptığım bu yanıta lütfen gönderimi bulun. Girişlerinizi gerçekten takdir ediyorum :)
Devendra Lattu

7

Yeterince ilginç bir şekilde, Groovy kodunda (bir sınıf dosyasına derlenebilir), çağrılardan biri hariç tümü print deyimini çalıştırır. (Bir Testi bir Nesne ile karşılaştıran kişi, Test.equals (Test) işlevini çağırmaz.) Bunun nedeni, groovy DOES'un tamamen dinamik yazım yapmasıdır. Bu özellikle ilgi çekicidir çünkü açıkça dinamik olarak yazılmış herhangi bir değişkene sahip değildir. Programcılar harika java olayını yapmasını beklediklerinden, bunun zararlı kabul edildiğini birkaç yerde okudum.


1
Ne yazık ki Groovy'nin ödediği fiyat, her yöntem çağrısı yansıma kullandığından, büyük bir performans düşüşü. Bir dilin diğeriyle tamamen aynı şekilde çalışmasını beklemek genellikle zararlı kabul edilir. Farklılıkların farkında olunması gerekir.
Joachim Sauer

JDK7'de invokedynamic ile güzel ve hızlı olmalı (hatta bugün benzer uygulama tekniğini kullanarak).
Tom Hawtin - tackline

5

Java, parametrelerde eş varyansı desteklemez, yalnızca dönüş türlerinde.

Başka bir deyişle, geçersiz kılma yöntemindeki dönüş türünüz, geçersiz kılınmış olanın bir alt türü olabilir, ancak bu, parametreler için doğru değildir.

Nesne'deki eşittir parametreniz Nesne ise, bir alt sınıftaki herhangi bir şeye eşitler koymak, geçersiz kılınmış bir yöntem değil, aşırı yüklenmiş bir yöntem olacaktır. Bu nedenle, bu yöntemin çağrılacağı tek durum, T3 durumunda olduğu gibi parametrenin statik türünün Test olması durumudur.

İş görüşmesi sürecinde bol şanslar! Öğrencilerime öğrettiğim alışılmış algo / veri yapıları soruları yerine bu tür sorular soran bir şirkette röportaj yapılmasını çok isterim.


1
Kontravaryant parametreleri kastediyorsunuz.
Tom Hawtin - tackline

Farklı yöntem parametrelerinin esasen aşırı yüklenmiş bir yöntem değil, aşırı yüklenmiş bir yöntem oluşturduğu gerçeğini bir şekilde tamamen geçiştirdim. Merak etmeyin, algo / veri yapıları soruları da vardı. : P Ve iyi şans için teşekkür ederim, buna ihtiyacım olacak! :)
Magsol

4

Bence anahtar, equals () yönteminin standarda uymaması gerçeğinde yatıyor: Başka bir Test nesnesi alıyor, Object nesnesini değil ve bu nedenle equals () yöntemini geçersiz kılmıyor. Bu, aslında ona sadece özel bir şey yapmak için aşırı yüklediğiniz anlamına gelir.Test nesnesi verirken ona Object nesnesi Object.equals (Object o) verir. Bu koda herhangi bir IDE aracılığıyla bakmak size Test için iki equals () yöntemi göstermelidir.


Bu ve yanıtların çoğu noktayı kaçırıyor. Mesele aşırı yüklemenin geçersiz kılmak yerine kullanılmasıyla ilgili değil. T1, Object olarak bildirildiğinde ancak Test olarak başlatıldığında, t1.equals (t3) için aşırı yüklenmiş yöntemin kullanılmamasının nedeni budur.
Robin

4

Yöntem geçersiz kılma yerine aşırı yüklenmiştir. Eşittir her zaman parametre olarak bir Nesne alır.

btw, Bloch'un etkili java'sında (sahip olmanız gereken) bu konuda bir öğeniz var.


Joshua Bloch'un Etkili Java'sı?
DJClayworth

Etkili evet, yazarken başka bir şey düşünüyordum: D
Gilles

4

Dynamic Binding (DD) ve Static Binding̣̣̣ (SB) bir süre aradıktan sonra bazı notlar :

1. Zamanlama yürütme : ( Ref. 1 )

  • DB: çalışma zamanında
  • SB: derleyici zamanı

2. Aşağıdakiler için kullanılır :

  • DB: geçersiz kılma
  • SB: aşırı yükleme (statik, özel, nihai) (Ref. 2)

Referans:

  1. Ortalama çözümleyiciyi çalıştırma hangi yöntemi kullanmayı tercih eder
  2. Çünkü statik, özel veya final değiştirici ile yöntemi geçersiz kılmak mümkün değildir.
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html

2

Aşırı yükleme yerine geçersiz kılan başka bir yöntem eklenirse, çalışma zamanında dinamik bağlama çağrısını açıklayacaktır.

/ * Aşağıdaki programın çıktısı nedir? * /

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

"Neden?" Sorusunun cevabı Java dili böyle tanımlanır.

Alıntı Kovaryans ve contravariance Wikipedia article :

Dönüş türü kovaryansı, Java programlama dili J2SE 5.0 sürümünde uygulanmaktadır. Yöntemin geçersiz kılınması için parametre türlerinin tamamen aynı (değişmez) olması gerekir, aksi takdirde yöntem bunun yerine paralel bir tanımla aşırı yüklenir.

Diğer diller farklıdır.


Benim problemim kabaca 3 + 3 görmek ve 9 yazmak, sonra 1 + 1 görmek ve 2 yazmakla eşdeğerdi. Java dilinin nasıl tanımlandığını anlıyorum; bu durumda, her ne sebeple olursa olsun, aynı problemde başka bir yerde bu hatadan kaçındığım halde, yöntemi olmadığı bir şeyle tamamen karıştırdım.
Magsol

0

Çok açık ki, burada geçersiz kılma kavramı yok. Yöntem aşırı yüklemesidir. Object()Nesne sınıf yöntemi nesne türü referans parametresi alır ve bu equal()yöntem tipi Testi referans parametresini alır.


-1

Bunu, internette karşılaştığım bazı örneklerin genişletilmiş versiyonları olan iki örnekle açıklamaya çalışacağım.

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

Burada, 0, 1, 2 ve 3 sayma değerlerine sahip satırlar için; Elimizdeki başvuru ait Nesne için o1 ve t1 üzerinde equals()yöntemle. Bu nedenle, derleme zamanında Object.class dosyasındaki equals()yöntem sınırlandırılacaktır.

Ancak, olsa bile referans ait t1 olan nesne , sahip olduğu intialization ait Testi sınıfına .
Object t1 = new Test();.
Bu nedenle, çalışma zamanında public boolean equals(Object other)hangisinin bir

geçersiz kılınan yöntem

. görüntü açıklamasını buraya girin

Şimdi, değerleri 4 ve 6 olarak saymak için , referansı olan t3'ün referansı ve başlatma Testi aradığını equals()nesne referans olarak parametresiyle yöntem ve bir bir

aşırı yüklenmiş yöntem

TAMAM!

Yine, derleyicinin hangi yöntemi çağıracağını daha iyi anlamak için, sadece yönteme tıklayın ve Eclipse, derleme zamanında çağıracağını düşündüğü benzer türlerdeki yöntemleri vurgulayacaktır. Derleme zamanında çağrılmazsa, bu yöntemler yöntemi geçersiz kılma örneğidir.

görüntü açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.