当前位置:  开发笔记 > 编程语言 > 正文

继承和递归

如何解决《继承和递归》经验,为你挑选了6个好方法。

假设我们有以下类:

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

}

现在让我们recursive在A班打电话:

public class Demo {

    public static void main(String[] args) {
        A a = new A();
        a.recursive(10);
    }

}

正如预期的那样,输出从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)

让我们来看看令人困惑的部分.现在我们打电话给recursiveB班.

预期:

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)

实际:

B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
..infinite loop...

这是怎么发生的?我知道这是一个设计的例子,但它让我很奇怪.

具体用例的旧问题.



1> Tunaki..:

这是预料之中的.这是一个实例发生的事情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
    }

}

因此,呼叫在A和之间交替B.

在实例的情况下不会发生这种情况,A因为不会调用overriden方法.



2> CoderCroc..:

因为recursive(i - 1);在第二种情况下A指的this.recursive(i - 1);B#recursive哪种.所以,superthis将在被称为递归函数交替.

void recursive(int i) {
    System.out.println("B.recursive(" + i + ")");
    super.recursive(i + 1);//Method of A will be called
}

A

void recursive(int i) {
    System.out.println("A.recursive(" + i + ")");
    if (i > 0) {
        this.recursive(i - 1);// call B#recursive
    }
}



3> Erick G. Hag..:

其他答案都解释了基本点,一旦实例方法被覆盖,它就会被覆盖,并且除了通过之外没有得到它super.B.recursive()调用A.recursive().A.recursive()然后调用recursive(),解析为覆盖B.我们来回打乒乓直到宇宙结束或者以StackOverflowError先到者为准.

这将是很好,如果能写this.recursive(i-1)A拿到自己的实现,但是这可能会打破东西,并有其他不幸的后果,所以this.recursive(i-1)A所调用B.recursive()等等.

有一种方法可以获得预期的行为,但它需要先见之明.换句话说,您必须提前知道您希望super.recursive()在实现中A陷入被困的子类型A.它是这样完成的:

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

由于A.recursive()调用doRecursive()并且doRecursive()永远不会被覆盖,因此A可以确保它调用自己的逻辑.


如果绝对必须允许继承,这正是Bloch在Effective Java中推荐的方法.第17项:"如果您认为必须允许从[未实现标准接口的具体类]继承,一种合理的方法是确保该类永远不会调用其任何可覆盖的方法并记录这一事实."

4> Eran..:

super.recursive(i + 1);在类B调用父类的方法明确,所以recursiveA被调用一次.

然后,recursive(i - 1);在类A中将调用recursiveB中覆盖recursive类的方法A,因为它是在类的实例上执行的B.

随后Brecursive会叫Arecursive明确,等等.



5> EpicPandaFor..:

这实际上不能采取任何其他方式.

当你打电话B.recursive(10);,然后打印B.recursive(10)然后调用此方法的实现Ai+1.

所以你打电话A.recursive(11),打印A.recursive(11)它调用recursive(i-1);的当前实例是方法B与输入参数i-1,所以它调用B.recursive(10),然后调用超级实现与i+111,然后以递归方式调用当前实例是递归的i-110,你会得到你在这里看到的循环.

这都是因为如果你在超类中调用实例的方法,你仍然会调用你正在调用它的实例的实现.

想象一下,

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

您将获得"BARK"而不是编译错误,例如"无法在此实例上调用抽象方法"或运行时错误AbstractMethodError甚至pure virtual method call或类似的东西.所以这一切都是为了支持多态性.



6> jonrsharpe..:

B实例的recursive方法调用super类实现时,正在执行的实例仍然是B.因此,当超类的实现在recursive没有进一步限定的情况下调用时,那就是子类实现.结果就是你所看到的永无止境的循环.

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有