Misalkan kita memiliki kelas-kelas berikut:
class A {
void recursive(int i) {
System.out.println("A.recursive(" + i + ")");
if (i > 0) {
recursive(i - 1);
}
}
}
class B extends A {
void recursive(int i) {
System.out.println("B.recursive(" + i + ")");
super.recursive(i + 1);
}
}
Sekarang mari panggil recursive
di kelas A:
public class Demo {
public static void main(String[] args) {
A a = new A();
a.recursive(10);
}
}
Outputnya, seperti yang diharapkan menghitung mundur dari 10.
A.recursive(10)
A.recursive(9)
A.recursive(8)
A.recursive(7)
A.recursive(6)
A.recursive(5)
A.recursive(4)
A.recursive(3)
A.recursive(2)
A.recursive(1)
A.recursive(0)
Mari kita ke bagian yang membingungkan. Sekarang kita sebut recursive
di kelas B.
Diharapkan :
B.recursive(10)
A.recursive(11)
A.recursive(10)
A.recursive(9)
A.recursive(8)
A.recursive(7)
A.recursive(6)
A.recursive(5)
A.recursive(4)
A.recursive(3)
A.recursive(2)
A.recursive(1)
A.recursive(0)
Sebenarnya :
B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
..infinite loop...
Bagaimana ini bisa terjadi? Saya tahu ini adalah contoh yang dibuat, tetapi itu membuat saya bertanya-tanya.
Pertanyaan lama dengan kasus penggunaan konkret .
java
inheritance
recursion
raupach
sumber
sumber
A
sebenarnya dikirim secara dinamis kerecursive
metode objek saat ini. Jika Anda bekerja dengan sebuahA
objek, panggilan tersebut membawa Anda keA.recursive()
, dan dengan sebuahB
objek, keB.recursive()
. TapiB.recursive()
selalu meneleponA.recursive()
. Jadi, jika Anda memulai suatuB
objek, itu beralih bolak-balik.Jawaban:
Ini diharapkan. Inilah yang terjadi untuk contoh
B
.class A { void recursive(int i) { // <-- 3. this gets called System.out.println("A.recursive(" + i + ")"); if (i > 0) { recursive(i - 1); // <-- 4. this calls the overriden "recursive" method in class B, going back to 1. } } } class B extends A { void recursive(int i) { // <-- 1. this gets called System.out.println("B.recursive(" + i + ")"); super.recursive(i + 1); // <-- 2. this calls the "recursive" method of the parent class } }
Karena itu, panggilan bergantian antara
A
danB
.Ini tidak terjadi dalam kasus instance
A
karena metode yang diganti tidak akan dipanggil.sumber
Karena
recursive(i - 1);
diA
mengacu padathis.recursive(i - 1);
yangB#recursive
di kasus kedua. Jadi,super
danthis
akan dipanggil dalam fungsi rekursif sebagai alternatif .void recursive(int i) { System.out.println("B.recursive(" + i + ")"); super.recursive(i + 1);//Method of A will be called }
di
A
void recursive(int i) { System.out.println("A.recursive(" + i + ")"); if (i > 0) { this.recursive(i - 1);// call B#recursive } }
sumber
Jawaban lain semuanya telah menjelaskan poin penting, bahwa setelah metode instance diganti, ia tetap diganti dan tidak ada yang mendapatkannya kembali kecuali melalui
super
.B.recursive()
memanggilA.recursive()
.A.recursive()
lalu memanggilrecursive()
, yang menyelesaikan penggantian diB
. Dan kita ping pong bolak-balik sampai akhir alam semesta atau aStackOverflowError
, mana yang lebih dulu.Akan lebih baik jika salah satu bisa menulis
this.recursive(i-1)
diA
untuk mendapatkan implementasi sendiri, tapi itu mungkin akan melanggar hal-hal dan memiliki konsekuensi yang tidak menguntungkan lainnya, sehinggathis.recursive(i-1)
dalamA
memanggilB.recursive()
dan sebagainya.Ada cara untuk mendapatkan perilaku yang diharapkan, tetapi itu membutuhkan kejelian. Dengan kata lain, Anda harus tahu sebelumnya bahwa Anda ingin
super.recursive()
subtipe dariA
terjebak, sehingga untuk berbicara, dalamA
implementasi. Ini dilakukan seperti:class A { void recursive(int i) { doRecursive(i); } private void doRecursive(int i) { System.out.println("A.recursive(" + i + ")"); if (i > 0) { doRecursive(i - 1); } } } class B extends A { void recursive(int i) { System.out.println("B.recursive(" + i + ")"); super.recursive(i + 1); } }
Sejak
A.recursive()
memanggildoRecursive()
dandoRecursive()
tidak pernah bisa diganti,A
yakinlah bahwa itu memanggil logikanya sendiri.sumber
doRecursive()
dalamrecursive()
dari objek ituB
berhasil. Seperti yang TAsk tulis dalam jawabannya, panggilan fungsi berfungsi sepertithis.doRecursive()
dan ObjectB
(this
) tidak memiliki metodedoRecursive()
karena berada di kelas yangA
didefinisikan sebagaiprivate
dan tidakprotected
dan karena itu tidak akan diwarisi, bukan?B
tidak bisa menelepondoRecursive()
sama sekali.doRecursive()
adalahprivate
ya. Tapi saatB
panggilansuper.recursive()
, itu memanggil implementasirecursive()
dalamA
, yang memiliki akses kedoRecursive()
.super.recursive(i + 1);
di kelasB
memanggil metode kelas super secara eksplisit, jadirecursive
dariA
dipanggil sekali.Kemudian,
recursive(i - 1);
di kelas A akan memanggilrecursive
metode di kelasB
yang menggantikanrecursive
kelasA
, karena itu dijalankan pada sebuah instance kelasB
.Kemudian
B
'srecursive
akan memanggilA
' srecursive
eksplisit, dan sebagainya.sumber
Itu sebenarnya tidak bisa dilakukan dengan cara lain.
Ketika Anda memanggil
B.recursive(10);
, kemudian mencetakB.recursive(10)
kemudian memanggil implementasi metode iniA
dengani+1
.Jadi Anda memanggil
A.recursive(11)
, yang mencetakA.recursive(11)
yang memanggilrecursive(i-1);
metode pada instance saat ini yangB
dengan parameter inputi-1
, sehingga memanggilB.recursive(10)
, yang kemudian memanggil implementasi super dengani+1
yang11
, yang kemudian secara rekursif memanggil instance saat ini yang rekursifi-1
dengannya10
, dan Anda akan dapatkan lingkaran yang Anda lihat di sini.Ini semua karena jika Anda memanggil metode instance di superclass, Anda masih akan memanggil implementasi instance yang Anda panggil.
Bayangkan ini,
public abstract class Animal { public Animal() { makeSound(); } public abstract void makeSound(); } public class Dog extends Animal { public Dog() { super(); //implicitly called } @Override public void makeSound() { System.out.println("BARK"); } } public class Main { public static void main(String[] args) { Dog dog = new Dog(); } }
Anda akan mendapatkan "BARK" sebagai ganti error kompilasi seperti "metode abstrak tidak dapat dipanggil pada instance ini" atau error runtime
AbstractMethodError
atau bahkanpure virtual method call
atau semacamnya. Jadi ini semua untuk mendukung polimorfisme .sumber
Saat metode
B
instancerecursive
memanggilsuper
implementasi kelas, instance yang ditindaklanjuti masih dalamB
. Oleh karena itu, ketika implementasi kelas super memanggilrecursive
tanpa kualifikasi lebih lanjut, itulah implementasi subkelas . Hasilnya adalah lingkaran tanpa akhir yang Anda lihat.sumber