在一个类中,什么时候应该使用 "this"?

360

我知道this指的是当前对象,但我不知道什么时候需要使用它。例如,在某些方法中,如果我使用x代替this.x,是否会有任何区别?也许x将引用只在该方法中可见的局部变量?

那么this.method()呢?我可以使用它吗?我应该使用它吗?如果我只使用method(),默认情况下它不会被应用于当前对象吗?

17个回答

463

this 关键字主要有三种使用情况。第一种也是最常见的情况是在 setter 方法中用来消除变量引用的歧义。第二种情况是当需要将当前类实例作为参数传递给另一个对象的方法时使用。第三种情况是在构造函数内部调用其他构造函数的一种方式。

情况 1: 使用 this 来消除变量引用的歧义。在 Java 的 setter 方法中,我们通常会传入与私有成员变量相同名称的参数。然后,我们将参数 x 赋值给 this.x。这样可以清楚地表明正在将参数 "name" 的值赋给实例变量 "name"。

public class Foo
{
    private String name;

    public void setName(String name) {
        this.name = name;
    }
}

情况2:this用作传递给另一个对象的参数。

public class Foo
{
    public String useBarMethod() {
        Bar theBar = new Bar();
        return theBar.barMethod(this);
    }

    public String getName() {
        return "Foo";
    }
}

public class Bar
{
    public void barMethod(Foo obj) {
        obj.getName();
    }
}

情况三: 使用this调用其他构造函数。在评论中,trinithis正确指出了this的另一个常见用法。当您为单个类拥有多个构造函数时,您可以使用this(arg0, arg1, ...)来调用您选择的另一个构造函数,前提是您在构造函数的第一行这样做。

class Foo
{
    public Foo() {
        this("Some default value for bar");

        //optional other lines
    }

    public Foo(String bar) {
        // Do something with bar
    }
}

我也看到过在实例变量被引用时使用this来强调这一事实(无需消除歧义),但在我看来这种情况很少见。

我认为这种使用方式很罕见,主要是为了强调使用的是实例变量而不是其他变量。


33
谢谢您提供的翻译机会。针对这句话,我的翻译如下:「+1 是因为提到你也可以将 this 作为参数传递。this 不仅用于作用域消歧义。」 - Alex Jasmin
12
当然,在构造函数中也可以使用this(arg1, arg2, ...) - Thomas Eding
14
@Hazior:我倾向于先写一个简短的答案,然后随着时间的推移添加更多内容。有时这会与其他人的答案重叠,有时不会。在最新的编辑中,trinithis指出了我忘记了另一种常见使用this的情况,因此我将其添加到了我的答案中。我认为这样做没有任何问题,因为最终结果是一个更好的答案,这正是SO的目的。我也尽可能地给予信用,就像我在trinithis的情况下所做的那样。 - William Brendel
4
你有第1和第3种情况的例子。你能否举一个第2种情况的例子,其中当前类实例被用作另一个类的方法的参数? - dbconfession
5
在我多年的Java代码库工作中,只有在确实需要消除歧义时才会使用“this”,例如上面我的setter示例。当然,编码风格和“最佳实践”因人而异,但通常情况下,我建议选择合理的模式并坚持遵循它们。一致性,甚至仅在单个代码库内部,对于可读性和可维护性都有很大帮助。 - William Brendel
显示剩余10条评论

81

this的第二个重要用途(除了像许多答案已经提到的使用局部变量隐藏之外),是在从嵌套非静态类访问外部实例时使用:

public class Outer {
  protected int a;

  public class Inner {
    protected int a;

    public int foo(){
      return Outer.this.a;
    }

    public Outer getOuter(){
      return Outer.this;
    }
  }
}

51

当存在与本地变量名称重名的情况时,您只需要使用this - 大多数人都只使用它。 (例如,setter方法)

当然,使用this的另一个好处是它会在IDE中弹出智能感知提示 :)


4
但是在你查找完之后,你必须要回退它。编程真的很累人! - LegendLength

32

只有当当前范围内的另一个变量与实例成员同名,并且您想要引用该实例成员时,才需要使用 this. 限定符(正如William所描述的那样)。除此之外,xthis.x 的行为没有区别。


3
如果您有重复的名称,其中一个变量应该被重新命名,因为它几乎肯定命名不当。或者至少,可以更好地命名。 - CaffGeek
3
在Java的setter方法中,这是常见做法。然而,在setter方法之外,你的陈述通常成立。 - William Brendel
2
你可能想要使用 this.x 使你的代码更易读。此外,代码的可维护性和可读性也是你应该考虑的因素之一。 - Astra
2
@Blair:阅读你的回答让人清楚,你不喜欢在setter方法中使用这种做法,但很多人都这样做(我也是其中之一)。如果我有一个接受值的setter方法,那么传入的值显然应该是“新”的值,因此在变量名中添加“new”似乎为公共API增加了不必要的冗余。 - Adam Robinson
1
显然,你不能有两个同名的成员。这就是为什么你最初的评论是多余和太过吹毛求疵的原因。想想你可以在方法中创建一个与类变量同名的变量。 - LegendLength
显示剩余7条评论

15

在一个构造函数中调用另一个构造函数时,关键字"this"会很有用:

public class MyClass {
    public MyClass(String foo) {
        this(foo, null);
    }
    public MyClass(String foo, String bar) {
        ...
    }
}

14

有很多好的答案,但是还有一个非常微不足道的原因要在每个地方使用this关键字。如果您尝试使用普通文本编辑器(例如记事本等)打开源代码,则使用this将使其更加清晰易读。

想象一下:

public class Hello {
    private String foo;

    // Some 10k lines of codes

    private String getStringFromSomewhere() {
        // ....
    }

    // More codes

    public class World {
        private String bar;

        // Another 10k lines of codes

        public void doSomething() {
            // More codes
            foo = "FOO";
            // More codes
            String s = getStringFromSomewhere();
            // More codes
            bar = s;
        }
    }
}

使用任何现代集成开发环境都可以轻松阅读,但在常规文本编辑器中阅读将是一场噩梦。

你将努力寻找foo的位置,直到使用编辑器的“查找”功能。然后你会因为同样的原因而对getStringFromSomewhere()大声抱怨。最后,在你忘记s是什么之后,bar = s将给你最后的打击。

与此相比:

public void doSomething() {
    // More codes
    Hello.this.foo = "FOO";
    // More codes
    String s = Hello.this.getStringFromSomewhere();
    // More codes
    this.bar = s;
}
  1. 您知道foo是在外部类Hello中声明的变量。
  2. 您知道getStringFromSomewhere()也是在外部类中声明的方法。
  3. 您知道bar属于World类,而s是在该方法中声明的局部变量。

当然,在设计任何东西时,都需要创建规则。因此,如果您的API或项目的规则包括“如果有人使用记事本打开所有这些源代码,他或她应该朝自己的头开枪”,那么您完全可以不用做这件事


很棒的回答 @Jai - Gaurav
1
第一个需要“开枪”的原因是编写具有数万行代码的类,尤其是如果代码已经分割为不需要嵌套的不同类。 :) - LuCio
@LuCio 哈哈,确实 xD - Jai

13

this 在建造者模式中非常有用。

public class User {

    private String firstName;
    private String surname;

    public User(Builder builder){
        firstName = builder.firstName;
        surname = builder.surname;
    }

    public String getFirstName(){
        return firstName;
    }

    public String getSurname(){
        return surname;
    }

    public static class Builder {
        private String firstName;
        private String surname;

        public Builder setFirstName(String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder setSurname(String surname) {
            this.surname = surname;
            return this;
        }

        public User build(){
            return new User(this);
        }

    }

    public static void main(String[] args) {
        User.Builder builder = new User.Builder();
        User user = builder.setFirstName("John").setSurname("Doe").build();
    }

}

1
这正是我在搜索并最终来到这里时想要的答案类型,但你没有解释你的代码,所以大多数询问“this”的人将不理解“return new user(this);”的含义,就像我一样... - nckbrz
建造者模式用于在构造过程中清晰地指定参数。而不是使用没有明显标识的 new User(string, string),你可以使用 new Builder().setFirstName("Jane").setSurname("Smith").build()。你可以从 Builder.set...() 函数返回此对象,以便可以链式调用它们。 - ChrisPhoenix

8
除非您有重叠的变量名称,否则这只是为了在阅读代码时更清晰明了。

2
当你看到不必要的时候经常出现“this”关键字,它只是样板代码,使代码更难阅读。 - AxeEffect
我刚刚发现了一个开源项目,要求所有成员都以“this”为前缀。除此之外,该项目写得非常好,但我很想与他们进行宗教辩论。 - LegendLength
10
@AxeEffect 我知道这已经很老了,但是……“this”不会让代码难以阅读,哈哈。 - Xatenev

4

@William Brendel的回答以清晰的方式提供了三种不同的用例。

用例1:

官方Java文档页面this提供了相同的用例。

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

它涵盖了两个示例:

使用Field使用Constructor

用例2:

此帖子中未引用的其他用例:this可用于同步多线程应用程序中的当前对象,以保护关键数据和方法的临界区。

synchronized(this){
    // Do some thing. 
}

用例3:

构建器模式的实现取决于使用this来返回修改后的对象。

请参考此帖子

将构建器保留在单独的类中(流接口)


2

谷歌搜索出了Sun网站上讨论这个问题的页面。

你对于变量的理解是正确的;this确实可以用来区分方法变量和类字段。

    private int x;
    public void setX(int x) {
        this.x=x;
    }

然而,我真的很讨厌这种约定。给两个完全相同的变量命名是引发错误的一个因素。我更喜欢以下的写法:

    private int x;
    public void setX(int newX) {
        x=newX;
    }

结果相同,但没有机会在你打算引用x时意外地引用了x

至于在方法中使用它,你对于效果的理解是正确的;你将得到相同的结果,无论是否使用它。你能使用它吗?当然可以。应该使用它吗?由你决定,但考虑到我个人认为它是毫无意义的冗余,不会增加任何清晰度(除非代码塞满了静态导入语句),我不倾向于自己使用它。


4
这不是一种约定,而是一种程序语言的作用域机制。你列出的使用newX(我更喜欢使用pX表示参数x)是一种约定。 - Bill K
@Bill K:我不明白你所做的区分。我可以选择将输入变量命名为x、newX、pX、mangroveThroatWarblerX等。如果选择给它一个与它设置的变量名称相同的名称,这与在其前面添加“new”、“p”或“Gratuitous Monty Python References”是不是一样的惯例呢? - BlairHippo
3
“免费的蒙提·派森参考”不是一种惯例,而是法律。 - Adam Robinson
为此,我们在参数和方法变量中使用不同的命名规范,而与类变量不同。我们缩写参数/方法变量并对类/实例变量使用完整单词。 - Lawrence Dol
1
通过使用命名约定来解决它,嗯,这是一种约定。通过使用语言特性来解决它 - 我想选择从不使用该语言特性或始终使用它将是一种约定...每次访问成员时都使用“this”将是一种约定。我猜这没什么关系,我也讨厌“.this”。 - Bill K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接