Java динамично обвързване и отмяна на метод

Вчера имах двучасово техническо интервю по телефона (което преминах, уаууу!), но напълно заглуших следния въпрос относно динамичното обвързване в Java. И това е двойно озадачаващо, защото преди няколко години преподавах тази концепция на студенти, когато бях TA, така че перспективата да им дам дезинформация е малко смущаваща...

Ето проблема, който ми беше даден:

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

Твърдих, че изходът трябваше да бъде два отделни израза за печат от отменения метод equals(): в t1.equals(t3) и t3.equals(t3). Последният случай е достатъчно очевиден и с първия случай, въпреки че t1 има препратка от тип Object, той се инстанцира като тип Test, така че динамичното обвързване трябва да извика заменената форма на метода.

Очевидно не. Моят интервюиращ ме насърчи сам да стартирам програмата и ето, имаше само един изход от заменения метод: на ред t3.equals(t3).

Въпросът ми тогава е защо? Както вече споменах, въпреки че t1 е препратка от тип Object (така че статичното обвързване би извикало метода equals() на Object), динамичното обвързване трябва да се погрижи за извикването на най-специфичната версия на метода въз основа на инстанцирания вид на справката. какво ми липсва


person Magsol    schedule 26.11.2008    source източник
comment
Моля, намерете публикацията ми към този отговор, където се опитах да обясня с допълнителни случаи. Наистина ще бъда благодарен за вашите приноси :)   -  person Devendra Lattu    schedule 03.04.2017


Отговори (12)


Java използва статично обвързване за претоварени методи и динамично обвързване за заменени. Във вашия пример методът equals е претоварен (има различен тип параметър от Object.equals()), така че извиканият метод е обвързан с типа reference по време на компилиране.

Някои дискусии тук

Фактът, че това е методът на равенствата, не е наистина уместен, освен че е често срещана грешка да се претоварва вместо да се отменя, за което вече сте наясно въз основа на вашия отговор на проблема в интервюто.

Редактиране: Добро описание тук също. Този пример показва подобен проблем, свързан вместо това с типа параметър, но причинен от същия проблем.

Вярвам, че ако обвързването беше действително динамично, тогава всеки случай, когато извикващият и параметърът бяха екземпляр на Test, ще доведе до извикването на заменения метод. Така че t3.equals(o1) ще бъде единственият случай, който няма да се отпечата.

person Robin    schedule 26.11.2008
comment
Много хора посочват, че е претоварен и не е отменен, но дори и с това бихте очаквали да разреши правилно претоварения. Вашият пост всъщност е единственият досега, който отговаря правилно на въпроса, доколкото мога да преценя. - person Bill K; 27.11.2008
comment
Грешката ми беше, че напълно липсваше фактът, че методът наистина е претоварен, а не заменен. Видях equals() и веднага си помислих за наследено и заменено. Изглежда, че отново съм разбрал правилната по-широка и трудна концепция, но съм прецакал простите детайли. :P - person Magsol; 27.11.2008
comment
друга причина анотацията @Override съществува. - person Matt; 16.03.2012
comment
Повторете след мен: Java използва статично обвързване за претоварени методи и динамично обвързване за заменени - +1 - person Mr_and_Mrs_D; 30.11.2013
comment
така че завърших, без да знам това. Благодаря! - person Atieh; 18.12.2014
comment
Моля, намерете публикацията ми към този отговор, където се опитах да обясня с допълнителни случаи. Наистина ще бъда благодарен за вашите приноси :) - person Devendra Lattu; 03.04.2017

Методът equals на Test не заменя метода equals на java.lang.Object. Вижте типа параметър! Класът Test претоварва equals с метод, който приема Test.

Ако методът equals е предназначен за отмяна, той трябва да използва анотацията @Override. Това би причинило грешка при компилиране, за да посочи тази често срещана грешка.

person erickson    schedule 26.11.2008
comment
Да, не съм съвсем сигурен защо пропуснах този прост, но важен детайл, но точно там беше проблемът ми. Благодаря ти! - person Magsol; 27.11.2008
comment
+1 за това, че е истинският отговор на любопитните резултати на питащия - person matt b; 27.11.2008
comment
Моля, намерете публикацията ми към този отговор, където се опитах да обясня с допълнителни случаи. Наистина ще бъда благодарен за вашите приноси :) - person Devendra Lattu; 03.04.2017

Интересното е, че в кода на Groovy (който може да бъде компилиран във файл на клас), всички извиквания освен едно ще изпълнят оператора за печат. (Този, който сравнява тест с обект, очевидно няма да извика функцията Test.equals(Test).) Това е така, защото groovy ПРАВИ напълно динамично писане. Това е особено интересно, защото няма променливи, които са изрично динамично въведени. Четох на няколко места, че това се смята за вредно, тъй като програмистите очакват Groovy да направи нещото с java.

person Benson    schedule 26.11.2008
comment
За съжаление цената, която Groovy плаща за това, е огромен удар в производителността, тъй като всяко извикване на метод използва отражение. Очакването един език да работи точно като друг обикновено се счита за вредно. Човек трябва да е наясно с разликите. - person Joachim Sauer; 26.11.2008
comment
Трябва да е хубаво и бързо с invokedynamic в JDK7 (или дори с използване на подобна техника за внедряване днес). - person Tom Hawtin - tackline; 26.11.2008

Java не поддържа ко-вариация в параметри, само в типове връщане.

С други думи, докато вашият върнат тип в отменен метод може да е подтип на това, което е бил в отменения, това не е вярно за параметрите.

Ако вашият параметър за equals в Object е Object, поставянето на equals с нещо друго в подклас ще бъде претоварен, а не отменен метод. Следователно единствената ситуация, в която този метод ще бъде извикан, е когато статичният тип на параметъра е Test, както в случая с T3.

Успех в процеса на интервю за работа! Бих се радвал да бъда интервюиран в компания, която задава този тип въпроси вместо обичайните въпроси за алгоритми/структури от данни, на които преподавам на студентите си.

person Uri    schedule 26.11.2008
comment
Имате предвид контравариантни параметри. - person Tom Hawtin - tackline; 26.11.2008
comment
Някак си напълно премълчах факта, че различните параметри на метода по същество създават претоварен метод, а не отменен. О, не се притеснявайте, имаше и въпроси относно алгоритмите/структурите на данни. :P И благодаря за късмета, ще ми трябва! :) - person Magsol; 27.11.2008

Мисля, че ключът се крие във факта, че методът equals() не отговаря на стандарта: той приема друг тестов обект, а не обект Object и по този начин не отменя метода equals(). Това означава, че всъщност сте го претоварили само, за да направи нещо специално, когато му е даден Test обект, докато му давате Object обект извиква Object.equals(Object o). Разглеждането на този код през която и да е IDE трябва да ви покаже два метода equals() за Test.

person P Arrayah    schedule 26.11.2008
comment
Това и повечето от отговорите не съдържат смисъла. Проблемът не е във факта, че се използва претоварване вместо преодоляване. Ето защо не се използва претовареният метод за t1.equals(t3), когато t1 е деклариран като Object, но инициализиран за Test. - person Robin; 27.11.2008

Методът е претоварен вместо отменен. Equals винаги приема обект като параметър.

между другото, имате елемент за това в ефективната Java на Bloch (която трябва да притежавате).

person Gilles    schedule 26.11.2008
comment
Ефективната Java на Джошуа Блок? - person DJClayworth; 26.11.2008
comment
Ефективно да, мислех за нещо друго, докато пишех :D - person Gilles; 27.11.2008

Някои бележки в Динамично обвързване (DD) и Статично обвързване̣̣̣(SB) след известно търсене:

1. Време за изпълнение: (Спр. 1)

  • DB: по време на изпълнение
  • SB: време на компилатор

2. Използва се за:

  • DB: отмяна
  • SB: претоварване (статично, лично, окончателно) (Спр. 2)

Справка:

  1. Изпълнете средство за преобразуване кой метод предпочитате да използвате
  2. Тъй като не може да се замени метод с модификатор static, private или final
  3. http://javarevisited.blogspot.com/2012/03/what-is-static-and-dynamic-binding-in.html
person NguyenDat    schedule 16.03.2012

Ако се добави друг метод, който замества вместо претоварване, той ще обясни извикването на динамично обвързване по време на изпълнение.

/* Какъв е резултатът от следната програма? */

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);
    }
}
person Prabu R    schedule 30.10.2013

Намерих интересна статия за динамично срещу статично свързване. Той идва с част от кода за симулиране на динамично обвързване. Това направи кода ми по-четлив.

https://sites.google.com/site/jeffhartkopf/covariance

person Bijan    schedule 12.07.2012

Отговорът на въпроса "защо?" така се дефинира езикът Java.

За да цитирам статията в Уикипедия за ковариацията и контравариацията:

Ковариацията на връщания тип е внедрена във версията на езика за програмиране Java J2SE 5.0. Типовете параметри трябва да бъдат абсолютно еднакви (инвариантни) за отмяна на метода, в противен случай методът се претоварва с паралелна дефиниция вместо това.

Другите езици са различни.

person ykaganovich    schedule 26.11.2008
comment
Проблемът ми беше приблизително еквивалентен на това да видя 3+3 и да напиша 9, след това да видя 1+1 и да напиша 2. Разбирам как се дефинира езикът Java; в този случай, по някаква причина, напълно обърках метода с нещо, което не беше, въпреки че избегнах тази грешка другаде в същия проблем. - person Magsol; 27.11.2008

Много ясно е, че тук няма концепция за пренебрегване. Това е претоварване на метода. методът Object() на клас Object приема референтен параметър от тип Object, а този equal() метод взема референтен параметър от тип Test.

person ankush gatfane    schedule 16.11.2010

Ще се опитам да обясня това чрез два примера, които са разширените версии на някои от примерите, на които попаднах онлайн.

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

Тук за редове със стойности на броя 0, 1, 2 и 3; имаме референция на Обект за o1 и t1 на метода equals(). Така по време на компилиране методът equals() от файла Object.class ще бъде ограничен.

Въпреки това, въпреки че препратката на t1 е Object, тя има инициализация на Тестов клас.
Object t1 = new Test();.
Следователно по време на изпълнение той извиква public boolean equals(Object other), което е an

отменен метод

. въведете описание на изображението тук

Сега, за стойности на броя като 4 и 6, отново е ясно, че t3, който има препратка и инициализация на Test, извиква equals() метод с параметър като препратки към обекти и е

претоварен метод

OK!

Отново, за да разберете по-добре какъв метод ще извика компилаторът, просто щракнете върху метода и Eclipse ще маркира методите от подобен тип, които смята, че ще извика по време на компилиране. Ако не бъде извикан по време на компилиране, тогава тези методи са пример за заместване на метод.

въведете описание на изображението тук

person Devendra Lattu    schedule 03.04.2017