Значение "this" в анонимном классе против лямбда-выражения

Я немного смущен различным поведением анонимного класса и лямбда-выражения.

когда я использую лямбда-выражение:

//Test.java

Runnable r1 = () -> System.out.println(this);
Runnable r2 = () -> System.out.println(toString());

@Override
public String toString() {
    return "Hello World!";  
}

// in main method 
new Test().r1.run();
new Test().r2.run();   
Output : Hello World!
         Hello World!

при использовании анонимного класса:

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println(this);   
    }
};

Runnable r2 = new Runnable() {
    @Override
    public void run() {
        System.out.println(toString()); 
    }
};

@Override
public String toString() {
    return "Hello World!";  
}

// in main method 
new Test().r1.run();
new Test().r2.run();  
Output : Package_Name.Test@1db9742
         Package_Name.Test@106d69c

может кто-нибудь объяснить другое поведение?

1 ответов


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

спецификация языка Java описывает это поведение на 15.27.2:

в отличие от кода, появляющегося в анонимных объявлениях классов, значение имен и this и super ключевые слова, появляющиеся в лямбда-теле, а также доступность ссылки объявления, такие же, как и в окружающем контексте (за исключением того, что лямбда-параметры вводят новые имена).

прозрачность this (как явное, так и неявное) в теле лямбда - выражения - то есть, рассматривая его так же, как и в окружающем контексте-обеспечивает большую гибкость для реализаций и предотвращает зависимость значения неквалифицированных имен в теле от разрешения перегрузки.

практически говоря, для лямбда-выражения необычно говорить о себе (либо называть себя рекурсивно, либо вызывать другие методы), в то время как более распространено желание использовать имена для обозначения вещей в заключающем классе, которые в противном случае были бы затенены (this, toString()). Если необходимо, чтобы лямбда-выражение ссылалось на себя (как если бы через this), следует использовать ссылку на метод или Анонимный внутренний класс вместо.

для справки this окружающего класса изнутри анонимного класса вам придется использовать квалификации this.

Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println(Test.this); // or Test.this.toString()
    }
};