java-类如何具有自己的类型的成员,这不是无限递归吗?
假设我定义了一个类,该类具有与自己相同类型的变量作为成员。
public class abc {
private abc p;
}
这实际上有效,令我惊讶的是。
为什么我不应该这样:创建一个abc实例,它包含abc类型的变量,其中包含abc类型的变量,其中包含abc类型的变量,...
显然我错了,有人可以启发我吗?
jason asked 2020-08-06T16:32:29Z
7个解决方案
36 votes
您仅声明变量而不创建它。 尝试在声明或构造函数中创建它,让我知道会发生什么:
public class Abc {
private Abc p = new Abc(); // have fun!
public static void main(String[] args) {
new Abc();
}
}
顺便说一句,如果您不是在类中创建它,而是在getter方法或构造函数参数中接受对它的引用,则您的代码将正常工作。 这就是某些链接列表的工作方式。
Hovercraft Full Of Eels answered 2020-08-06T16:32:39Z
20 votes
区别在于编译时检查与运行时检查。
在第一种情况(编译时)中,您声明在这种情况下将引用类型为2999663452953753773056的值。 编译器在检查适当的语义时将意识到这一点,并且由于它在编译时就知道类型,因此认为没有问题。
在第二种情况下(运行时),您实际上将创建一个值供此引用引用。 在这里您可能会遇到麻烦。 例如,如果您说以下:
public class abc {
private abc p;
public abc() {
p = new abc();
}
}
这可能会因所引用的确切原因而给您带来麻烦(递归不包含任何基本情况,并且会不断分配内存,直到将VM用完堆空间为止)。
但是,您仍然可以执行类似的操作并避免无限递归。 通过避免在构造过程中创建值,可以推迟使用它,直到需要使用方法为止。 实际上,这是在Java中实现单例模式的常见方法之一。 例如:
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;
}
}
这是完全有效的,因为您只创建了一个新的值实例,并且由于运行时已经加载了该类,因此它将知道实例变量的类型是有效的。
pseudoramble answered 2020-08-06T16:33:22Z
10 votes
当您要对一些实际场景进行建模时,可能必须使用此概念。 例如,考虑一棵树的分支。 一棵树的分支上可能有n个分支。 或从计算机科学背景出发,想到一个链表的Node。 节点将引用其旁边的节点。 最后,下一个将包含一个空值,以指示列表的结尾。
因此,这仅是参考,表明此类型可能引用其自身的一种。 而已。
ring bearer answered 2020-08-06T16:33:47Z
1 votes
在这里总结一些答案。
该类包含对自己的同类的引用。现实世界中最好的类比是寻宝。 一个线索位置中包含一些数据和一个线索,这会导致另一个线索位置,您再次知道重复该线索位置。
并不是说一个线索位置中有另一个线索位置,您似乎可以推断出那种线索位置。
Rakesh Horkeri answered 2020-08-06T16:34:17Z
1 votes
如选择的答案所述,这是可以的,因为没有实例化变量,而是接受了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的新对象。 静态就是这样工作的,它们几乎不受对象实例化的影响,在您的问题中,它们是递归规则的例外。
WTRIX answered 2020-08-06T16:35:00Z
1 votes
它的基本用法可以是数据结构中的链接列表。 它是链接列表中的核心概念,是从一个节点到另一节点的引用。 类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个下一个→NULL
因此,这只是在链接列表的数据结构中将一个节点连接到另一个节点的引用类型。
LEO CHEN answered 2020-08-06T16:35:33Z
0 votes
当我们谈论“递归”时,代码的“执行”是我们考虑的重点。 是的,如果发生“递归”,则它是“动态的”。
在类型声明中,包含成员的变量(即包含变量的类型)为“静态”。 例如,一个盒子可能在其中包含另一个盒子,依此类推。 但最后有一个最小的盒子,里面什么也不装。 对于一连串的铁路车厢,除最后一个车厢外,每个车厢都有一个成员。
在程序中,静态数据必须是“有限的”(您没有“无限”的存储空间)。 代码的执行可以是“无限的”。 但是有一个例外。 只是想像一个链环。
Mike Lue answered 2020-08-06T16:36:03Z