在Java中的方法调用接受的实践中传递“ this”


93

在方法调用中传递当前对象是好/坏/可接受的做法。如:

public class Bar{
    public Bar(){}

    public void foo(Baz baz){
        //  modify some values of baz
    }
}

public class Baz{
    //constructor omitted

    public void method(){
        Bar bar = new Bar();
        bar.foo(this);
    }
}

具体来说,该行bar.foo(this)可接受吗?


60
为什么那不能接受?很常见
DenysSéguret13年

3
所以...那就是8是:)(是的,我一直在更新它。)
Alex Gittemeier

2
非静态匿名类会自动传递超类的此引用,因此这是可以接受的。唯一的警告是注意循环引用。
Mehul Rathod

5
但是有一个警告:不要在构造函数中传递它,因为那样会使对象处于不一致状态。人们通常在创建回调(例如ActionListener)作为匿名内部类并将其传递给另一个对象时执行此操作。
塔玛斯牧师

3
@dystroy尽管我同意它是可以接受的,但它暗示它是可以接受的,因为它很常见,这是很糟糕的逻辑。做某事是因为它的
Carrie Kendall

Answers:


155

没有理由不使用它,它this是当前实例,并且使用完全合法。实际上,通常没有清除它的干净方法。

因此使用它。

由于难以令人信服地没有示例是可以接受的(对这个问题的否定答案总是更容易争论),我只是打开了最常见的java.langString之一,当然,例如,我找到了这种用法的实例

1084        // Argument is a String
1085        if (cs.equals(this))
1086            return true;

查找(this在大“接受”的项目,你不会不发现它。


6
完美的答案。
maxf130

15
-1是因为没有提到双向类关系比单向关系复杂的事实。确保软件尽可能清晰至关重要。在上面的特定示例中,将方法 foo移至Baz类以避免在两个类之间具有双向引用并将行为和数据放在一起会更明智。
JW。

35
-1为答案,因为您在问题中发现了可以评论的其他次要细节?真的吗
DenysSéguret13年

13
@dystroy是,我不同意您的开场白:“没有理由不使用它”。
JW。

4
实际上,在实际代码中(与OP的简化示例相反),传递this并不意味着您要添加双向链接,例如由于继承和接口。
DenysSéguret13年

165

没有错。什么是不是一个好的做法是做同样的内部构造,因为你会给一个还未完全初始化对象的引用。

这里有种类似的文章:Java在构造函数泄漏了此内容,他们其中解释了为什么后者是一种不良做法。


18
+1:很高兴指出在构造函数中引用“ this”的危害。
拔示巴2013年

这不一定是坏习惯。例如,一个Car构造函数可以创建Wheel实例中,Car没有Wheel将被完全初始化,而Wheel没有相应的Car也将完全初始化。在这种情况下,可以在Car的构造函数中传递this给Wheel的构造函数。另一种选择是使Car和Wheel具有私有构造函数,并使用工厂函数构造Car,Wheel,并将Wheel安装在Car上。但这应该是Car上的静态方法还是Wheel上的静态方法?
Lie Ryan

显然,您应该创建一个CarFactoryWheelInstallerProxy为您安装轮子的。
凯文

6
@LieRyan Wheel完全从属于Car,并且IMO根本不应该知道Car
Izkata

3
this在构造函数中使用的唯一不好的事情是,如果this将其传递到方法或上下文中,则尚未完全构造的对象引用从该方法或上下文中发布到不受信任或未知的客户端(或假定客户端代码具有对视图的视图的客户端代码)完全构造的对象)。this我认为,从构造函数传递到执行私有初始化的package-private方法不仅是可接受的,而且是合乎需要的。
斯科特,

42

是的,但是您应该注意两点

  1. 在尚未构造对象时(即在其构造函数中) 传递此参数
  2. 将此对象传递给一个长期存在的对象,该对象将使引用保持活动状态,并防止对该对象进行垃圾回收。

1
请注意,Java中的构造函数并不是真正的构造函数,将Java的构造函数称为“ initializer”可能更合适。在Java构造函数中,实际上已经为对象分配了内存,而该对象实际上已经在构造函数中存在/构造了。
Lie Ryan

1
并非完全如此,对象可能具有尚未初始化的某些实例变量,因此该对象尚未完全起作用。因此,在构造函数中,您可以将其传递给第二个对象,该对象可以调用尚未初始化其所有实例变量的对象的方法。
Stefanos T.

但是,只要第二个对象知道未初始化传递的对象,并将其视为不透明的对象,或仅以这种状态调用已声明安全的方法,则不会有任何问题传递this给它。如果没有分配对象,那么这样做是不可能的。
Lie Ryan


5

这代表当前对象。您在做什么在语法上是正确的,但是如果您在同一类中调用该方法,我认为不需要这样做。


2
在示例代码中,Baz.method()是一个实例方法,该方法使用Baz的实例作为参数调用Bar.foo()。因此,OP不会在同一类中调用方法。
CVn

@MichaelKjörling我认为Juned在说,通过将foo()方法移到Baz类中,就不需要this在两个类之间传递。因此,无需增加额外的复杂性。
JW。

4

如果没有更简单的替代方法可以实现相同的行为,则在方法调用中传递当前对象是一种不好的做法

根据定义,双向关联会在this从一个对象传递到另一个对象后立即创建。

引用马丁·福勒的《重构》一书:

将双向关联更改为单向(200)

双向关联很有用,但要付出一定的代价。代价是维护双向链接并确保正确创建和删除对象会增加复杂性。双向关联对于许多程序员而言并不自然,因此它们经常是错误的根源

...

在需要时应使用双向关联,而在不需要时则应使用双向关联。一旦看到双向关联不再增加其权重,就放弃不必要的一端。

因此,从理论上讲,当我们发现需要通过时,我们应该听到警钟,this并努力尝试其他解决当前问题的方法。当然,在某些情况下,这样做是有道理的。

此外,在长期重构代码以整体改进的过程中,经常有必要临时破坏设计,做一些“不好的做法”。(后退一步,前进两步)。

在实践中,我发现通过避免像瘟疫这样的双向链接,我的代码得到了极大的改进。


您将简化示例与建立双向链接的需求混淆了。将其作为参数传递,正如java.lang源代码的许多示例(例如,您在我的答案中看到的示例)中应该清楚的那样,并不意味着您添加了双向依赖性。我认为这个答案应该是评论。
DenysSéguret13年

@dystroy感谢您添加评论以解释您为什么投票。总是很高兴知道。我将修改我的答案以澄清,根据定义,双向关联会在this通过后立即创建。
JW。

1
“根据定义,双向关联会在通过后立即创建”。这可以清楚地说明您无法理解的地方。看我举的例子。没有双向链接,因为其中的参数类型equals是Object。这是很常见的:接收方法将其参数定义为更通用的类或接口。在Java中使用这种模式的原因之一避免不必要的依赖性。在继续之前,我建议您看一下在受人尊敬的Java库中作为参数传递的许多情况this
DenysSéguret13年

我们只是同意不同意。因为我尽可能避免传递this代码,所以我的生活变得更加轻松。我建议其他人这样做。
JW。

2
@JW:这个答案中给出的推理是无关紧要的。它总是一个好主意,做X,如果有别的东西,很简单,对于X的任意值
李瑞安

4

是。您可以使用它,这在编程中很常见,this但是使用它有优缺点,但这样做并不危险。


有很多副作用。它增加了复杂性。
JW。

如果有那么多副作用,我们找不到像Java源代码中那样的证据。请参阅源代码中的@destroys示例。
Suresh Atta

2

仅添加一个示例,其中传递this正确且遵循良好设计:访问者模式。在Visitor设计模式中,方法accept(Visitor v)通常以调用的方式实现v.visit(this)


1

可以接受

Oracle JAVA文档的摘录:

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

与字段一起使用

使用this关键字的最常见原因是因为方法或构造函数参数对字段造成了阴影。


2
“您可以引用当前对象的任何成员 ”-似乎没有回答“是否可以作为参数传递this ”的问题。
CVn

2
这就是说,您该怎么做this.some_variable才能引用类变量而不是局部变量。它与传递this参数无关。
Jose Salvatierra

0

Java中的所有内容均按值传递。但是对象永远不会传递给方法!
当java将对象传递给方法时,它首先创建对该对象的引用的副本,而不是对象本身的副本。因此,这是在Java中完美使用的方法。最常用的用法。


9
这看起来很主题。
DenysSéguret13年

4
“ java中的所有内容都是按值传递的。” -这是一个非常令人误解的初步评论。实际上,所有对象都是通过引用传递的,所有基本类型都是通过值传递的。您从没有对this原始类型的引用,因此我认为您的“更多信息”正在引起混乱。
斯图尔特

1
@Stewart:他立即明确表示他并不意味着要复制整个对象。
LarsH 2013年

10
@Stewart不,对象不是通过引用传递的,而是对象引用是通过值传递的。这是一个重要的区别-通过引用传递意味着,如果我有一个引用对象的局部变量并将该变量传递给另一个方法,则该方法将能够更改我的变量引用的对象,这绝对不是什么你可以用Java来做。该方法可以通过其自己的引用副本改变对象的状态,但不能更改我的引用副本以指向其他对象。
伊恩·罗伯茨

2
@Stewart yoda.arachsys.com/csharp/parameters.html是一篇很好的文章,它解释了在同时支持引用和C#的情况下按引用传递和按值传递引用之间的区别。
伊恩·罗伯茨
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.