Почему мы не можем вызвать Thread # sleep () непосредственно внутри лямбда-функции? - PullRequest
0 голосов
/ 02 октября 2018

Следующий код дает мне ошибку во время компиляции:

Thread t2 = new Thread(() -> {
    try { 
        sleep(1000);
    } 
    catch (InterruptedException e) {}
});

Метод sleep (int) не определен для типа A (где A - мой классname).

Принимая во внимание, что при использовании анонимного внутреннего класса ошибки времени компиляции не возникает:

Thread t1 = new Thread(){
    public void run(){
        try {
            sleep(1000);
        } catch (InterruptedException e) {}
    }
};

Приведенный ниже код также отлично работает:

Thread t3 = new Thread(() -> System.out.println("In lambda"));

Как все работает в теле лямбда-выражения?Пожалуйста, помогите.

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

Ответы [ 8 ]

0 голосов
/ 04 октября 2018
public void foo() {
    new Thread(() -> { sleep(1000); });
}

эквивалентно

public void foo() {
    new Thread(this::lambda$0);
}
private void lambda$0() {
    sleep(1000);
}

, поэтому компилятор не будет искать sleep в Thread

0 голосов
/ 02 октября 2018

Thread.sleep является статическим методом в классе Thread.

Причина, по которой вы можете вызвать sleep напрямую без каких-либо определителей в анонимном классе, заключается в том, что вы на самом деле находитесь в контексте классачто наследуется от Thread.Следовательно, sleep доступен там.

Но в случае лямбды вы не принадлежите к классу, который наследует от Thread.Вы находитесь внутри любого класса, окружающего этот код.Следовательно, sleep не может быть вызван напрямую, и вам нужно сказать Thread.sleep.Документация также поддерживает это:

Лямбда-выражения имеют лексическую область видимости.Это означает, что они не наследуют никаких имен от супертипа или вводят новый уровень видимости.Объявления в лямбда-выражении интерпретируются так же, как и во внешней среде.

По сути, это говорит о том, что внутри лямбды вы фактически находитесь в той же области видимости, как если бы вы были вне лямбда-выражения.Если вы не можете получить доступ к sleep за пределами лямбды, вы также не можете получить доступ и к внутренней части.

Также обратите внимание, что два способа создания потока, которые вы здесь показали, по сути различны.В лямбда-1 вы передаете Runnable конструктору Thread, тогда как в анонимном первом классе вы создаете Thread, непосредственно создавая его анонимный класс.

0 голосов
/ 02 октября 2018

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

В случае AIC this относится к экземпляру класса, который вы расширяете (в вашем примере это Thread), в случае lambda expression, this относится к экземплярукласса, который окружает лямбда-выражение (каким бы ни был этот класс в вашем примере).Бьюсь об заклад, в вашем классе, где вы используете лямбда-выражение, не определено такое sleep.

0 голосов
/ 02 октября 2018

Работает следующий код:

    Thread t2 = new Thread(() -> {
        try { 
            Thread.sleep(1000);
        } 
        catch (InterruptedException e) {}
    });

Это потому, что sleep(int milliseconds) - это метод из класса Thread, когда вы создаете и передаете экземпляр Runnable в Thread конструктор класса.

Во втором методе вы создаете анонимный внутренний экземпляр класса Thread класса и, таким образом, имеете доступ ко всем Thread методам класса.

0 голосов
/ 02 октября 2018

В результате получается недопонимание Scope.

Когда вы передаете лямбду потоку, вы не создаете подкласс Thread, вместо этого вы передаете FunctionalInterface Runnable и вызов конструктора Thread.Когда вы пытаетесь вызвать Sleep, контекст вашей области представляет собой комбинацию Runnable + ваш класс (вы можете вызывать методы по умолчанию, если они есть в интерфейсе Runnable), а не Thread.

Runnable не имеет sleep ()определяется, но Thread делает.

Когда вы создаете анонимный внутренний класс, вы создаете подкласс Thread, поэтому функция sleep () доступна для вызова, поскольку контекст Scope является подклассом Thread.

Вызывать статические методы без имени класса не рекомендуется именно из-за такого недопонимания.Использование Thread.Sleep является правильным и однозначным при любых обстоятельствах.

0 голосов
/ 02 октября 2018

Ваше сомнение связано с неправильным пониманием того, как определяются области действия лямбда-выражения и анонимного класса.Ниже я попытаюсь прояснить это.

Лямбда-выражения НЕ вводят новый уровень видимости.Это означает, что внутри него вы можете получить доступ только к тем вещам, к которым вы сможете получить доступ в непосредственно включенном блоке кода.Посмотрите, что говорят документы :

Лямбда-выражения имеют лексическую область видимости.Это означает, что они не наследуют никаких имен от супертипа или вводят новый уровень видимости.Объявления в лямбда-выражении интерпретируются так же, как и во внешней среде.

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

Как и локальные классы, анонимные классы могут захватывать переменные;они имеют одинаковый доступ к локальным переменным охватывающей области:

  • Анонимный класс имеет доступ к членам своего включающего класса.
  • Анонимный класс не может получить доступ к локальным переменным в своемзаключающая область, которые не объявлены как окончательные или фактически окончательные.
  • Как и вложенный класс, объявление типа (такого как переменная) в анонимном классе затеняет любые другие объявления во внешней области действия, которые имеют то же самоеназвание.См. Shadowing для получения дополнительной информации.

В этом контексте анонимный класс будет действовать как локальный класс внутри Thread и, таким образом, он сможет напрямую обращаться к sleep(),так как этот метод будет в пределах своей области.Однако в лямбда-выражении sleep() не будет находиться в его пределах (вы не можете вызвать sleep() в окружающей среде), поэтому вы должны использовать Thread.sleep().Обратите внимание, что этот метод static и, следовательно, не требует экземпляра своего класса для вызова.

0 голосов
/ 02 октября 2018

При первом подходе вы передаете Runnable на Thread, вам нужно позвонить Thread.sleep:

Thread t2 = new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
});

это короткая версия:

Runnable runnable = new Runnable() {
    public void run(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}
    }
};

Thread t2 = new Thread(runnable);

На втором этапе вы переопределяете метод thread.run напрямую, поэтому можно вызывать thread.sleep в thread.run.

0 голосов
/ 02 октября 2018

Thread.sleep - статический метод ...

 Thread t2 = new Thread(() -> {
        try {
            Thread.sleep(1000);
        }
        catch (InterruptedException e) {}
    });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...