Java 8 lambda表达式,其抽象类只有一个方法

时间:2017-12-18 18:24:36

标签: java lambda java-8

我正在学习Java 8中的lambda表达式。有人可以向我解释如何将lambda表达式与只有一种方法的抽象类一起使用(如果可能的话)?

例如,这是抽象类:

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")));

显然该语句不起作用,但有没有办法在这里使用lambda表达式?如果有的话,我做错了什么?

3 个答案:

答案 0 :(得分:13)

不,您无法实现此目的,因为ClassA必须是功能界面。功能接口是一个只包含一个抽象方法的接口。除抽象方法外,它可以包含零个或多个默认方法和/或静态方法。由于功能接口只包含一个抽象方法,因此在使用lambda表达式实现该方法时,可以省略该方法的名称。例如,请考虑以下界面:

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);

请注意,这里没有花括号,也没有return关键字。这是可能的,因为该方法返回一个布尔值,表达式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;
    }
});

答案 1 :(得分:4)

您只能使用lambda表达式实例化一个功能接口。

例如:

如果您将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")));
由于以下几个原因,

尝试失败:

  1. 您无法实例化抽象类(您只能实例化该类的子类)。
  2. 即使您可以实例化类ClassA(如果它不是抽象的),您的ClassA也没有构造函数将某个函数接口作为参数,因此您不能这样做将() -> System.out.println("Hello")作为参数传递给ClassA的构造函数。

答案 2 :(得分:1)

好吧,你可以改变你的类,以便能够按照这些方式写一些东西:

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中实现抽象方法,请参阅上面的示例。