一个类如何拥有自己类型的成员,这不是无限递归吗?

时间:2012-03-20 02:47:15

标签: java oop class

假设我定义了一个类,该类具有与其自身相同类型的变量作为成员。

public class abc {
    private abc p;
}

这确实有效,令我惊讶。

为什么我认为它不应该:创建abc的实例,它包含abc类型的变量,其中包含abc类型的变量,其中包含一个变量输入abc,其中.....

显然我错了,有人可以告诉我怎么做?

7 个答案:

答案 0 :(得分:34)

您只是声明变量而不是创建变量。尝试在声明或构造函数中创建它,让我知道会发生什么:

public class Abc {
   private Abc p = new Abc(); // have fun!

   public static void main(String[] args) {
      new Abc();
   }
}

顺便说一句,如果你不在类中创建它,而是在getter方法或构造函数参数中接受对它的引用,那么你的代码就可以正常工作了。这是一些链表的工作方式。

答案 1 :(得分:19)

不同之处在于编译时与运行时检查。

在第一种情况(编译时)中,您声明在此实例中您将引用类型abc的值。编译器在检查正确的语义时会注意到这一点,并且因为它在编译时知道类型,所以它没有看到这个问题。

在第二种情况(运行时)中,您实际上将为此引用创建一个值以供参考。这是可能可能让您陷入困境的地方。例如,如果您说以下内容:

public class abc {
    private abc p;

    public abc() {
        p = new abc();
    }
}

这可能会因为您引用的确切原因而导致您遇到麻烦(递归不包含基本情况,并且会在您从堆空间中运行VM之前不断分配内存)。

但是,您仍然可以执行与此类似的操作并避免无限递归。通过避免在构造期间创建值,您可以将其关闭直到调用方法。实际上,这是在Java中实现the singleton pattern的常用方法之一。例如:

public class abc {
    private abc p;

    private abc() {  // Private construction. Use singleton method
    }

    public static synchronized abc getInstance() {
        if (p == null)
            p = new abc();

        return p;
    }
}

这是完全有效的,因为您只创建了一个新的值实例,并且由于运行时已经加载了该类,因此它将知道实例变量的类型是否有效。

答案 2 :(得分:10)

当您想要为某些真实场景建模时,您可能必须使用此概念。例如,想一下Tree的分支。树的分支可能有n个分支。或者从计算机科学背景中,想一下链表的节点。节点将引用其旁边的节点。最后,下一个将包含一个null以指示列表的结尾。

所以,这只是一个引用,表明这种类型可能引用它自己的一种。没什么。

答案 3 :(得分:1)

总结一些答案。

该课程包含对自己类型的引用。现实世界中最好的比喻是寻宝。一个线索的地方有一些数据和线索,这导致另一个线索的地方,你知道重复。

这并不是说一个线索的地方还有另一个线索,你似乎推断的那种。

答案 4 :(得分:1)

正如所选择的答案指出的那样,这是可以的,因为没有实例化变量,而是接受了set方法或构造函数参数的引用。因此,您看到的只是参考,只是地址。

但是,有一种实例化它的方法而不会引起噩梦循环,那就是将其声明为静态。由于静态成员不在对象级别而是在类级别,因此这种弯曲了递归规则。

所以...

public class abc
{
    public static abc p = new abc ();
}

工作正常,因为变量“ p”实际上不受abc类的实例影响。几乎等同于在另一个类中创建'p'对象。

请记住,我可以这样做...

 public class abc
 {
     public static abc p = new abc ();

     public static void main(String [] args)
     {
         abc.p;
     }
 }

没有在main方法中创建abc的新对象。静态就是这样工作的,它们几乎不受对象实例化的影响,对于您的问题,它们是递归规则的例外。

答案 5 :(得分:1)

此功能的基本用途可能是数据结构中的链接列表。它是链接列表中的核心概念,是从一个节点到另一节点的引用。 Node类由“链表”的基本元素组成。

class Node{
    private int value;
    private Node next; //reference, as a pointer to another node, →

    Node(){
        this.value=0;
        this.next=null;
    }
    Node(int value){
        this.value=value;
        this.next=null;
    }
    Node(int value,Node next){
        this.value=value;
        this.next=next;
    }
    public int getValue() {
        return this.value;
    }
    public void setValue(int value) {
        this.value=value;
    }
    public Node getNext() {
        return this.next;
    }
    public void setNext(Node next) {
        this.next=next;
    }
}

我们可以使用参考来连接这些节点。

class Linkedlist{
    Node head = new Node();
    Node one = new Node();
    Node two = new Node();

    // Assign data values 
    one.value = 1;
    two.value = 2;

    // Connect nodes 
    one.next = two;
    two.next = NULL;

    //Save address of first node in head
    head = one;
}

头→1个下一个→2个下一个→空

因此,这只是在链接列表的数据结构中将一个节点连接到另一个节点的引用类型。

答案 6 :(得分:0)

当我们谈论“递归”时,代码的“执行”是我们正在考虑的问题。是的,如果发生“递归”,它就是“动态的”。

在类型声明中,包含成员的变量是“静态”,该成员是包含变量的类型。例如,一个框可能包含其中的另一个框,依此类推。但最后有一个最小的盒子,里面没有任何东西。对于一系列铁路车厢,除最后一辆车外,每辆车都有一个下一个车厢的成员。

在程序中,静态数据必须是“有限的”(你没有“无限”的内存空间)。代码的执行可能是“无限的”。但有一个例外。只是对一圈链进行成像。

相关问题