Почему я должен использовать ключевое слово "this" для прямых ссылок?

Когда я использую this ключевое слово для доступа к нестатической переменной в классе, Java не дает никакой ошибки. Но когда я им не пользуюсь, Java выдает ошибку. Почему я должен использовать this?

Я знаю, когда обычно я должен использовать this, но этот пример сильно отличается от обычного использования.

Пример:

class Foo {
//  int a = b; // gives error. why ?
    int a = this.b; // no error. why ?
    int b;
    int c = b;

    int var1 = this.var2; // very interesting
    int var2 = this.var1; // very interesting
}

5 ответов

Решение

Переменные сначала объявляются, а затем присваиваются. Этот класс такой же, как этот:

class Foo {
    int a;
    int b;
    int c = b;

    int var1;
    int var2;

    public Foo() {
        a = b;

        var1 = var2;
        var2 = var1;
    }
}

Причина, по которой вы не можете сделать int a = b; это потому что b еще не определен во время создания объекта, но сам объект (т.е. this) существует со всеми своими переменными-членами.

Вот описание для каждого:

    int a = b; // Error: b has not been defined yet
    int a = this.b; // No error: 'this' has been defined ('this' is always defined in a class)
    int b; 
    int c = b;  // No error: b has been defined on the line before  

Полное описание приведено в разделе 8.3.3 Спецификации языка Java: "Прямые ссылки во время инициализации поля"

Прямая ссылка (ссылающаяся на переменную, которая еще не была объявлена ​​в тот момент) является ошибкой только в том случае, если выполняются следующие условия:

  • Объявление переменной экземпляра в классе или интерфейсе C появляется текстуально после использования переменной экземпляра;

  • Использование - это простое имя в инициализаторе переменной экземпляра C или в инициализаторе экземпляра C;

  • Использование не на левой стороне задания;

  • C является самым внутренним классом или интерфейсом, включающим использование.

См. Текст, выделенный жирным шрифтом: "использование - это простое имя". Простое имя - это имя переменной без дополнительной квалификации. В вашем коде b это простое имя, но this.b не является.

Но почему?

Причина в том, что текст в примере в JLS гласит:

"Указанные выше ограничения предназначены для улавливания во время компиляции циклических или иным образом искаженных инициализаций"

Другими словами, они позволяют this.b потому что они думают, что квалифицированная ссылка повышает вероятность того, что вы тщательно продумали то, что делаете, а просто использовали b вероятно означает, что вы допустили ошибку.

Это обоснование дизайнеров языка Java. Насколько я знаю, так ли это на практике, никогда не исследовалось.

Порядок инициализации

Чтобы расширить вышесказанное, со ссылкой на комментарий Dukeling по этому вопросу, используя квалифицированную ссылку this.b скорее всего, не даст вам желаемых результатов.

Я ограничиваю это обсуждение переменными экземпляра, потому что OP только ссылается на них. Порядок, в котором присваиваются переменные экземпляра, описан в JLS 12.5 Создание экземпляров нового класса. Необходимо принять во внимание, что сначала создаются конструкторы суперкласса, а код инициализации (присваивания и блоки инициализации) выполняются в текстовом порядке.

Так дано

int a = this.b;
int b = 2;

вы закончите с a быть ноль (значение b в то время, когда aинициализатор был выполнен) и b быть 2.

Даже более странные результаты могут быть достигнуты, если конструктор суперкласса вызывает метод, который переопределен в подклассе, и этот метод присваивает значение b,

Поэтому, в общем, неплохо бы поверить компилятору и либо изменить порядок полей, либо исправить основную проблему в случае циклической инициализации.

Если вам нужно использовать this.b чтобы обойти ошибку компилятора, вы, вероятно, пишете код, который будет очень трудно поддерживать после вас.

Вы представили 3 случая:

  1. int a = b; int b;
    Это дает ошибку, потому что компилятор будет искать b в памяти и его там не будет. но когда вы используете this Ключевое слово, то это явно указывает, что b определяется в области видимости класса, все ссылки на классы будут найдены, и, наконец, он найдет его.
  2. Второй сценарий довольно прост и, как я описал, b определяется в объеме до c и не будет проблем при поиске b в памяти.
  3. int var1 = this.var2;
    int var2 = this.var1;
    В этом случае нет ошибки, потому что в каждом случае переменная определена в классе, и назначение использует this который будет искать назначенную переменную в классе, а не только контекст, за которым она следует.

Для любого класса в Java this является ссылочной переменной по умолчанию (когда не указана конкретная ссылка), которую может предоставить либо пользователь, либо компилятор предоставит его внутри нестатического блока. Например

public class ThisKeywordForwardReference {

    public ThisKeywordForwardReference() {
        super();
        System.out.println(b);
    }

    int a;
    int b;

    public ThisKeywordForwardReference(int a, int b) {
        super();
        this.a = a;
        this.b = b;
    }

}

Вы сказали, что int a = b; // gives error. why ? дает ошибку времени компиляции, потому что b объявлен после a который является Illegal Forward Reference в Java и рассматривается как ошибка времени компиляции.

Но в случае methodsForward Reference становится законным

int a = test();
int b;

int test() {
    return 0;
}

Но в моем коде конструктор с аргументом объявлен до того, как a & b, но не дает никакой ошибки во время компиляции, потому что System.out.println(b); будет заменен на System.out.println(this.b); компилятором.

Ключевое слово this просто означает текущую ссылку на класс или ссылку, к которой обращаются метод, конструктор или атрибут.

A a1 = new A();  // Here this is nothing but a1
a1.test();  // Here this is again a1

Когда мы говорим a = this.b; это указывает на то, что b является атрибутом текущего класса, но когда мы говорим a = b; так как это не внутри нестатического блока this не будет присутствовать и будет искать ранее объявленный атрибут, которого нет.

Пожалуйста, ознакомьтесь со спецификацией языка Java: https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html

Это причина, ИМО: The usage is via a simple name.

Так что в этом случае вы должны указать имя с помощью this,

Другие вопросы по тегам