拥有一个自己作为场地的物体是否合理?

时间:2015-07-27 13:22:29

标签: java object field this

拥有一个像这样的字段的对象是否合理:

class Thing {

    Thing field;

    public Thing() {
        this.field = this;
    }
}

我不是在谈论一个具有相同类型字段的类,而是一个类,以便该类的每个实例都将其自身作为字段。 我刚刚在一些遗留代码中看到了这个(这个领域从未使用过),所以我很好奇。任何合法使用这个吗?

9 个答案:

答案 0 :(得分:38)

是的,虽然这种情况很少见。这在JDK中用于字段可能为this的情况,但可能不是。{/ p>

来自实现Collections.synchronizedCollection(c)

的类
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
    private static final long serialVersionUID = 3053995032091335093L;

    final Collection<E> c;  // Backing Collection
    final Object mutex;     // Object on which to synchronize

    SynchronizedCollection(Collection<E> c) {
        this.c = Objects.requireNonNull(c);
        mutex = this;
    }

    SynchronizedCollection(Collection<E> c, Object mutex) {
        this.c = Objects.requireNonNull(c);
        this.mutex = Objects.requireNonNull(mutex);
    }

在这种情况下,mutex可能是当前类,但是如果从现有的同步集合中获取此集合,则互斥锁可能不同。例如如果您致电Map.values()MapsynchronizedMap,则mutex将成为地图而非集合。

另一个例子是Throwable,默认情况下将其指向原因。

/**
 * The throwable that caused this throwable to get thrown, or null if this
 * throwable was not caused by another throwable, or if the causative
 * throwable is unknown.  If this field is equal to this throwable itself,
 * it indicates that the cause of this throwable has not yet been
 * initialized.
 *
 * @serial
 * @since 1.4
 */
private Throwable cause = this;

答案 1 :(得分:19)

我至少可以想到一个合理的例子。 e.g。

我有一个证书链,其中链中的每个链接都有对其父级的引用。在链的顶部,最后一个证书是自签名的,因此其父级是自己的。

简而言之,它实际上取决于您正在建模的问题空间。任何绝对主张声称不应该这样做是因为缺乏想象力。

public class Cert {
    public Cert parent;
}

public class SelfSignedCert extends Cert {
    public SelfSignedCert() {
        this.parent = this;
    }
}

答案 2 :(得分:11)

不,在我看来这是不合理的,因为每个对象都隐含地已经有这样一个字段:this。当然,对象有一个有时但不总是引用自身的字段(例如可能出现在循环链表中)是合理的,但问题是关于一个总是指对象的字段本身。

我见过代码,其中这样的字段用于从内部类引用(匿名)包含对象,但在这种情况下,这不是必需的。您可以使用ContainingClass.this。例如:

class A {
    class B {
        A getParent() {
            return A.this;
        }
    }
}

答案 3 :(得分:7)

JDK中的另一个例子 - java.lang.Throwable

private Throwable cause = this;

cause字段可以处于3个状态 - 未设置;设为null;设置为另一个Throwable。

实施者使用this来表示未设置状态。

更易读的策略可能是为取消设置

定义一个标记值
static Throwable UNSET = new Throwable();

private Throwable cause = UNSET;

当然,还有一个递归依赖 - UNSET.cause=? - 这是另一个有趣的话题。

答案 4 :(得分:5)

我能想到的唯一一种情况是某种重构。要创建一个示例,对象可能已经开始作为一个非常面向对象的静态方法的集合:

public static void doItToIt(Foo it) {
  if (it.getType().equals("bar")) {
    it.setSomeVariable(value);
    it.manipulate();
  } else {
    // do nothing
  }
}

...所以我们决定摆脱getType()并使其成为子类关系,并且在重构时,复制并粘贴了该东西,以使代码更清晰,更面向对象,因此需要:

public class Bar extends Foo {
    private Bar it = this;

    private String getType() {
      return "bar";
    }

    public void doItToIt() {
      if (it.getType().equals("bar")) {
        it.setSomeVariable(value);
        it.manipulate();
      } else {
        // do nothing
      }
    }
 }

如果只有这样的一个方法,最好使用局部变量:

public void doItToIt() {
  final Bar it = this;
  ...
}

但如果有多个,使用实例变量可以使代码更快地运行。

现在最好将it的所有实例替换为this(并删除getType(),然后删除if,依此类推)但作为一个中间步骤,它仍然是一个改进,如果有(或者被认为是)其他更重要的事情要做,它可能会留在那个状态。

另一种可能性是从类似于this在伪代码中具有名称的规范中复制伪代码,并且您故意尝试匹配伪代码结构以便于比较伪代码实现规范。 (而且,如果你有多种方法,那么使用局部变量会导致重复。)

但一般来说,不,有一个始终引用this的实例变量是多余的,因而无法明确。

答案 5 :(得分:5)

如果之后可以更改引用的对象,但是初始值为this(尽管这样的设计也应该重新访问,因为它可能表明Thing有更多的责任,这可能是合理的。比它应该)。

但是,如果引用的对象始终是this,那么这是不必要的并且令人困惑。

以下声明不仅令人困惑,而且有趣:)

private final Thing thing = this;

答案 6 :(得分:2)

不,因为当它具有关键字SELECT * FROM Data WHERE Data.Kvartal IN (SELECT Kvartal.Kvartal_text FROM Kvartal WHERE Kvartal.Kvartal_nummer = Data.Kvartal);

时,将自己作为字段会是多余的
  

在实例方法或构造函数中,this是对当前对象的引用 - 正在调用其方法或构造函数的对象。您可以使用this从实例方法或构造函数中引用当前对象的任何成员。“ - Javadocs Keyword "this"

答案 7 :(得分:2)

在iOS中,对象通常具有委托 - 通常是可以提供数据的另一个对象,或者可以执行使对象正常工作所需的其他事物,作为子类化的替代方法。如果一个对象实现了委托应该实现的所有功能,那么该对象可以是它自己的委托。不完全常见,但也不常见。

让我们说你有一个&#34;老板&#34;谁有一个&#34;秘书&#34;,一个&#34;咖啡机&#34;和#34;司机&#34;。老板显然可以写自己的信件,自己做咖啡并开车自己,但不是每个老板都这样做。因此,有时这些字段中的一个或多个将被设置为&#34;这个&#34;。

答案 8 :(得分:0)

使用库UI控件进行反射的情况。

在某些使用模式中完全合法。在未公开的企业框架中,我已经看到了像这个简化示例的XML:

<form>
    <comboBox listItems="order.LineItems" displayMember="description" valueMember="selfRef"/>
</form>

通过反射进行控件与值的绑定。如果您只对Integer ID valueMember感到满意,则不需要类成员引用对象本身。但是如果你想通过设计引用对象本身,那么空值("")不会被实现为特殊情况this,那么你需要将它保存在一个成员变量中{{{ 1}}在上面的例子中)。

相关问题