拥有一个以自身为场的对象是否合理?
Is it ever justified to have an object which has itself as a field?
是否有理由拥有一个将自身作为字段的对象,如下所示:
class Thing {
Thing field;
public Thing() {
this.field = this;
}
}
我不是在谈论具有相同类型字段的 class,而是 class 使得 class 的每个实例都有自己作为字段。
我刚刚在一些遗留代码中看到了这个(这个字段从未使用过)所以我很好奇。有任何合法用途吗?
不,在我看来这是不合理的,因为每个对象隐含地已经有这样一个字段:this
。当然,一个对象有一个有时但不总是引用自身的字段是合理的(例如,它可能出现在循环链表中),但问题是关于一个总是引用对象的字段本身。
我看到过代码,其中使用这样的字段能够从内部 类 引用来自(匿名)的包含对象,但在那种情况下也没有必要。您可以使用 ContainingClass.this
。例如:
class A {
class B {
A getParent() {
return A.this;
}
}
}
否,因为当它具有关键字 this
时,将其自身作为字段是多余的
Within an instance method or a constructor, this
is a reference to the current object — the object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by using this
." - Javadocs Keyword "this"
我至少能想到一个有道理的例子。例如
我有一个证书链,链中的每个 link 都有一个对其父级的引用。在链的顶部,最后一个证书是自签名的,所以它的父级就是它自己。
简而言之,这实际上取决于您建模的问题space。任何断言不应该这样做的断言都是缺乏想象力。
public class Cert {
public Cert parent;
}
public class SelfSignedCert extends Cert {
public SelfSignedCert() {
this.parent = this;
}
}
我能想到的唯一情况是某些类型的重构。举个例子,一个对象可能已经开始作为不是非常面向对象的静态方法的集合:
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
的实例变量是多余的,因此妨碍了清晰度。
是的,虽然这种情况很少见。这在 JDK 中用于字段可能是 this
但可能不是的情况。
来自实现 Collections.synchronizedCollection(c)
的 class
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
可能是当前的 class,但是如果此集合是从现有的同步集合中获取的,互斥量可能会有所不同。例如如果您调用 Map.values()
并且 Map
是 synchronizedMap
,则 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;
如果引用的对象可以在之后改变,那是合理的,但是初始值是this
(虽然这样的设计也应该重新考虑,因为它可能表明Thing
已经更多的责任)。
但是,如果引用的对象总是this
,那就没必要了,容易混淆。
下面的声明不仅令人困惑,而且很有趣:)
private final Thing thing = this;
JDK 中的另一个示例 - java.lang.Throwable
private Throwable cause = this;
cause
字段可以处于 3 种状态 - 未设置;设置为空;设置为另一个 Throwable。
实施者使用this
表示unset状态。
一个更具可读性的策略可能是为unset
定义一个标记值
static Throwable UNSET = new Throwable();
private Throwable cause = UNSET;
当然,还有一个递归依赖 - UNSET.cause=?
- 这是另一个有趣的话题。
在 iOS 中,对象通常有委托 - 通常是另一个对象,它可以提供数据,或者可以做使对象正常工作所需的其他事情,作为子类化的替代方法。如果对象本身实现了委托应实现的所有功能,则该对象可以是它自己的委托。不完全常见,但也不罕见。
假设您有一个 "boss",他有一个 "secretary"、一个 "coffee maker" 和一个 "driver"。老板显然可以自己写信,自己煮咖啡,自己开车,但不是每个老板都这样做。所以有时这些字段中的一个或多个会被设置为"this"。
库 UI 控件的反射案例。
它在某些使用模式下是完全合法的。在未公开的企业框架中,我看到 XML 像这个简化的例子:
<form>
<comboBox listItems="order.LineItems" displayMember="description" valueMember="selfRef"/>
</form>
通过 Reflection 将控件绑定到值。如果您对 Integer ID
和 valueMember
感到满意,则不需要引用对象本身的 class 成员。但是如果你想引用对象本身并且通过设计,空值 (""
) 没有作为特殊情况实现,意思是 this
,那么你需要将它保存在一个成员变量中 (selfRef
在上面的例子中)。
是否有理由拥有一个将自身作为字段的对象,如下所示:
class Thing {
Thing field;
public Thing() {
this.field = this;
}
}
我不是在谈论具有相同类型字段的 class,而是 class 使得 class 的每个实例都有自己作为字段。 我刚刚在一些遗留代码中看到了这个(这个字段从未使用过)所以我很好奇。有任何合法用途吗?
不,在我看来这是不合理的,因为每个对象隐含地已经有这样一个字段:this
。当然,一个对象有一个有时但不总是引用自身的字段是合理的(例如,它可能出现在循环链表中),但问题是关于一个总是引用对象的字段本身。
我看到过代码,其中使用这样的字段能够从内部 类 引用来自(匿名)的包含对象,但在那种情况下也没有必要。您可以使用 ContainingClass.this
。例如:
class A {
class B {
A getParent() {
return A.this;
}
}
}
否,因为当它具有关键字 this
Within an instance method or a constructor,
this
is a reference to the current object — the object whose method or constructor is being called. You can refer to any member of the current object from within an instance method or a constructor by usingthis
." - Javadocs Keyword "this"
我至少能想到一个有道理的例子。例如
我有一个证书链,链中的每个 link 都有一个对其父级的引用。在链的顶部,最后一个证书是自签名的,所以它的父级就是它自己。
简而言之,这实际上取决于您建模的问题space。任何断言不应该这样做的断言都是缺乏想象力。
public class Cert {
public Cert parent;
}
public class SelfSignedCert extends Cert {
public SelfSignedCert() {
this.parent = this;
}
}
我能想到的唯一情况是某些类型的重构。举个例子,一个对象可能已经开始作为不是非常面向对象的静态方法的集合:
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
的实例变量是多余的,因此妨碍了清晰度。
是的,虽然这种情况很少见。这在 JDK 中用于字段可能是 this
但可能不是的情况。
来自实现 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
可能是当前的 class,但是如果此集合是从现有的同步集合中获取的,互斥量可能会有所不同。例如如果您调用 Map.values()
并且 Map
是 synchronizedMap
,则 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;
如果引用的对象可以在之后改变,那是合理的,但是初始值是this
(虽然这样的设计也应该重新考虑,因为它可能表明Thing
已经更多的责任)。
但是,如果引用的对象总是this
,那就没必要了,容易混淆。
下面的声明不仅令人困惑,而且很有趣:)
private final Thing thing = this;
JDK 中的另一个示例 - java.lang.Throwable
private Throwable cause = this;
cause
字段可以处于 3 种状态 - 未设置;设置为空;设置为另一个 Throwable。
实施者使用this
表示unset状态。
一个更具可读性的策略可能是为unset
定义一个标记值static Throwable UNSET = new Throwable();
private Throwable cause = UNSET;
当然,还有一个递归依赖 - UNSET.cause=?
- 这是另一个有趣的话题。
在 iOS 中,对象通常有委托 - 通常是另一个对象,它可以提供数据,或者可以做使对象正常工作所需的其他事情,作为子类化的替代方法。如果对象本身实现了委托应实现的所有功能,则该对象可以是它自己的委托。不完全常见,但也不罕见。
假设您有一个 "boss",他有一个 "secretary"、一个 "coffee maker" 和一个 "driver"。老板显然可以自己写信,自己煮咖啡,自己开车,但不是每个老板都这样做。所以有时这些字段中的一个或多个会被设置为"this"。
库 UI 控件的反射案例。
它在某些使用模式下是完全合法的。在未公开的企业框架中,我看到 XML 像这个简化的例子:
<form>
<comboBox listItems="order.LineItems" displayMember="description" valueMember="selfRef"/>
</form>
通过 Reflection 将控件绑定到值。如果您对 Integer ID
和 valueMember
感到满意,则不需要引用对象本身的 class 成员。但是如果你想引用对象本身并且通过设计,空值 (""
) 没有作为特殊情况实现,意思是 this
,那么你需要将它保存在一个成员变量中 (selfRef
在上面的例子中)。