см. пример выше.

от вопрос уже есть ответ здесь:

Лямбда-выражения для абстрактных классов 2 ответа

Я изучаю лямбда-выражения в Java 8. Может ли кто-нибудь объяснить мне, как использовать лямбда-выражения с абстрактным классом, имеющим только один метод (если это возможно)?

Например, это абстрактный класс:

public abstract class ClassA {

    public abstract void action();

}

И у меня есть другой класс, который принимает в своем конструкторе экземплярClassA:

public ClassB {
   public ClassB(String text, ClassA a){
      //Do stuff
    }
}

Поэтому мне было интересно, как написать что-то вроде этого:

ClassB b = new ClassB("Example", new ClassA(() -> System.out.println("Hello")));

Очевидно, что оператор не работает, но есть ли способ использовать лямбда-выражение здесь или нет? Если есть, что я делаю не так?

 Oliver Charlesworth18 дек. 2017 г., 19:26
Вы смотрели, например, наdocs.oracle.com/javase/tutorial/java/javaOO/...?
 Sean Van Gorder18 дек. 2017 г., 21:16
Вы, вероятно, хотите использовать встроенныйRunnable вместоClassA.
 JB Nizet18 дек. 2017 г., 19:26
Нет. Лямбды можно использовать только для реализации функционального интерфейса.

Ответы на вопрос(3)

Решение Вопроса

Нет, вы не можете этого достичь, потому чтоClassA должен бытьфункциональный интерфейс, Функциональный интерфейс - это интерфейс, который содержит ровно один абстрактный метод. Он может содержать ноль или более стандартных методов и / или статических методов в дополнение к абстрактному методу. Поскольку функциональный интерфейс содержит ровно один абстрактный метод, вы можете опустить имя этого метода, когда реализуете его с помощью лямбда-выражения. Например, рассмотрим следующий интерфейс:

interface Predicate<T> {
    boolean test(T t);
}

Цель этого интерфейса - предоставить метод, который работает с объектом классаT и вернутьboolean, Вы могли бы иметь метод, который принимает экземпляр класса, который реализует этот интерфейс, определенный следующим образом:

public void printImportantData(ArrayList<Data> dataList, Predicate<Data> p) {
    for (Data d : dataList) {
        if (p.test(d)) {
            System.out.println(d);
        }
    }
}

гдеData Класс может быть таким простым, как:

public class Data {
    public int value;
}

Теперь вы можете вызвать вышеуказанный метод следующим образом:

 printImportantData(al, (Data d) -> { return d.value > 1; }); 

Обратите внимание, что у метода здесь нет имени. Это возможно, потому что интерфейс имеет только один абстрактный метод, поэтому компилятор может выяснить имя. Это может быть сокращено до:

printImportantData(al, (Data d) -> d.value > 1);

Обратите внимание, что здесь нет фигурных скобок и возвратного ключевого слова. Это возможно, потому что метод возвращает логическое значение и выражениеd.value > 1 также возвращает логическое значение. Таким образом, компилятор может выяснить, что значение этого выражения должно быть возвращено из метода. Это может быть сокращено еще больше, чтобы:

printImportantData(al, d -> d.value > 1);

Обратите внимание, что нет описания типаd! Компилятор может выяснить всю информацию, которая ему нужна, потому что интерфейс имеет только один абстрактный метод, а этот метод имеет только один параметр. Таким образом, вам не нужно писать все эти вещи в вашем коде.
Сравните вышеприведенный подход со старым стилем Java 7, используя внутренний класс, который фактически делает то же самое:

printImportantData(al, new Predicate<Data>() {
    public boolean test(Data d) {
        return d.value > 1;
    }
});
 navy197818 дек. 2017 г., 19:35
Спасибо за ваш ответ. Поэтому я буду использовать интерфейс в том случае, если ClassA - это интерфейс только с одним параметром, я могу использовать лямбду. ;)
 navy197819 дек. 2017 г., 08:50
читая ответ на вопрос, я пометил свой ответ как «дубликат»:stackoverflow.com/questions/34424410/... Я немного запутался, видимо, вы можете использовать Lambda даже с обычными классами, а не только с функциональным интерфейсом, это правда?
 Alex Mamo19 дек. 2017 г., 10:46
@ navy1978 В этом ответе есть пример относительноRunnable, Как видим, это тожеfunctional interface а не класс бастракт. У него точно один абстрактный метод,run(), На мой взгляд, это не правильный пример.
 Alex Mamo18 дек. 2017 г., 19:51
Пожалуйста! Да, какfunctional interface, Ура!

-выражением.

Например:

Если вы изменитеClassA быть интерфейсом (вы, вероятно, захотите переименовать его):

public interface ClassA {
    public abstract void action();
}

и хранитьClassB как есть:

public class ClassB {
   public ClassB(String text, ClassA a){
      //Do stuff
    }
}

Вы можете передать экземпляр (интерфейс)ClassA кClassB конструктор с:

ClassB b = new ClassB("Example", () -> System.out.println("Hello"));    

С другой стороны, ваш

ClassB b = new ClassB("Example", new ClassA(() -> System.out.println("Hello")));

попытка не удалась по нескольким причинам:

Вы не можете создать экземпляр абстрактного класса (вы можете создавать экземпляры только подклассов этого класса).Даже если бы вы могли создать экземпляр классаClassA (если это не было абстрактно), вашClassA не имеет конструктора, который принимает некоторый функциональный интерфейс в качестве аргумента, поэтому вы не можете передать() -> System.out.println("Hello") в качестве аргументаClassAконструктор.
 navy197819 дек. 2017 г., 08:32
Первый пункт неверен: «Вы не можете создать экземпляр абстрактного класса», потому что вы даже не можете создать экземпляр интерфейса, но с внутренним классом вы можете сделать и то, и другое. Я дал тебе +1 в любом случае, потому что твой ответ для остальных правильный.
 Eran19 дек. 2017 г., 09:03
@ navy1978 ответ на этот вопрос создает экземпляр конкретного класса - он называетсяAbstractLambda но это не абстрактно. И лямбда-выражение передается в качестве параметра конструктору, как реализация функционального интерфейса -Supplier.
 navy197819 дек. 2017 г., 08:46
Я бы добавил, что если лямбда-код был создан для использования абстрактных классов, а также то, что вы написали: "ClassB b = new ClassB (" Example ", () -> System.out.println (" Hello "));" будет правильным способом использовать его в обоих случаях ...
 Eran19 дек. 2017 г., 08:37
Что не так с утверждением"You cannot instantiate an abstract class"? «но с внутренним классом вы можете сделать и то, и другое» - под «внутренним классом» вы имеете в виду экземпляр анонимного класса? Это было бы примеромПодкласс абстрактного класса. OP не создает анонимный экземпляр класса в своей неудачной попытке. Они пытаются создать экземпляр самого абстрактного класса, что невозможно. @ navy1978
 navy197819 дек. 2017 г., 08:43
Да, я имел в виду анонимный класс, извините, только что проснулся ... Мне не ясно, почему вы указали, что использование абстрактного класса отличается от использования интерфейса в моем случае, я просто думаю, что это просто вопрос реализации Lambda, потому что это может быть реализовано для управления абстрактными классами. с моей точки зрения, используя анонимные классы, вы можете создать интерфейс или абстрактный класс. Я не понимаю вашу точку зрения, извините ...

вы могли бы изменить свои классы, чтобы иметь возможность писать что-то вроде этого:

public static void main(String[] args) {
    ClassB b = new ClassB("Example", new ClassA(() -> System.out.println("Hello")) {

        @Override
        public void action() {

        }
    });
}

Но для этого вам нужно изменить конструкторClassA принять некоторую реализацию функционального интерфейса, напримерjava.lang.Runnable:

public abstract class ClassA {

    public ClassA(Runnable runnable) {
        runnable.run();
    }

    public abstract void action();

}

class ClassB {
    public ClassB(String text, ClassA a) {
        //Do stuff
    }
}

Конечно, вам все еще нужно реализовать абстрактный метод вClassAсм. пример выше.

Ваш ответ на вопрос