其中';Java中功能接口的威力是什么?
我正在阅读有关函数式编程的基础知识。无论如何,我举了一些例子试图理解这个概念并正确使用它 我不懂这种函数式编程的威力。它是否只是在编写lambda而不是普通代码 如果我们有这门课:其中';Java中功能接口的威力是什么?,java,lambda,java-8,functional-programming,Java,Lambda,Java 8,Functional Programming,我正在阅读有关函数式编程的基础知识。无论如何,我举了一些例子试图理解这个概念并正确使用它 我不懂这种函数式编程的威力。它是否只是在编写lambda而不是普通代码 如果我们有这门课: public class Dinosaurio { private boolean esMamifero; private String nombre; public Dinosaurio(String n, boolean esMam) {...} //getters and setters
public class Dinosaurio {
private boolean esMamifero;
private String nombre;
public Dinosaurio(String n, boolean esMam) {...}
//getters and setters
此功能接口:
@FunctionalInterface
public interface DinosaurioTester {
boolean test(Dinosaurio d);
}
而这一主要类别:
public class LambdaMain {
public static void main(String[] args) {
List<Dinosaurio> lista = new ArrayList<>();
lista.add(new Dinosaurio("Manolo", true));
lista.add(new Dinosaurio("Pepe", true));
lista.add(new Dinosaurio("Paco", false));
lista.add(new Dinosaurio("Curro", true));
lista.add(new Dinosaurio("Nemo", false));
pintadorDinosaurios(lista, a->a.isEsMamifero());
}
public static void pintadorDinosaurios(List<Dinosaurio> ld, DinosaurioTester dt) {
for(Dinosaurio d : ld) {
if(dt.test(d)) {
System.out.println(d.getNombre());
}
}
}
}
与此相反:
if(d.isEsMamifero())
Edit:确保这个示例对于函数式编程的实际使用来说是一个糟糕的示例代码。但这是我现在能想到的所有问题。在我看来,这似乎是一个非常常见的问题,尽管它有一个非常简单的答案。
对我来说,这是关于你如何看待你设计的合同,以及你期望它如何实施和使用 正如您所注意到的,您的示例显示了一种使用函数接口的糟糕方式。设计这些类型只是为了调用
if(dt.test(d))
而不是if(d.isemamifero())
需要一个简单的形容词:bad。这可能是教科书造成的。问题是,大多数书籍教我们如何使用函数式接口,就像教我们一般使用接口/抽象一样,而这忽略了函数式编程的要点和大局。当然,我们需要知道“如何”实现功能接口(毕竟它是一个接口),但许多书没有告诉我们在哪里应用它们
下面是我如何向自己解释的(用非常基本的术语):
1-将功能接口视为“命名逻辑”(在合同另一方实施)
是的,功能接口是类型,但将功能接口命名为逻辑更有意义。与普通类型(如Serializable
,Collection
,AutoCloseable
)不同,功能接口(如Tester
(或谓词
)表示逻辑(或仅表示代码)。我知道这些细微差别正在变得越来越微妙,但我相信在传统的OOP抽象“类型”和功能接口的含义之间是有区别的
2-将实现功能接口的代码与使用它的代码隔离开来
在同一组件中使用这两个组件的问题在您的代码中显而易见。您不会编写一个函数接口,声明一个使用函数接口的方法,所有这些都只是为了实现函数接口并将其传递给您自己的方法。如果您正在这样做,并且仅此而已,那么您使用抽象的原因是错误的,更不用说正确地使用功能接口了
有很多正确使用功能接口的例子。我将使用消费者选择集合。forEach
:
Collection<String> strings = Arrays.asList("a", "b", "c");
strings.forEach(s -> System.out.println(s));
Collection strings=Arrays.asList(“a”、“b”、“c”);
strings.forEach->System.out.println;
这与您的设计有何不同
Collection.forEach
的设计者在接受消费者的合约时停止(他们不使用消费者
,只是为自己的方法命名/键入参数
Collection.forEach
是一个需要自定义逻辑的操作。与
s->System.out.println
一样,
此“自定义逻辑”可以是
s->myList.add
或
s->myList.add(s.toUpperCase())、
等,所有这些都是根据客户机的设计者(在接口设计很久之后)。集合接口的forEach
方法编排迭代,并允许调用方提供在每次迭代中调用的逻辑。这分离了关注点
Stream.filter
与谓词
更接近您的示例,最好将您如何使用Stream.filter
与示例中如何使用Tester.test
进行对比
尽管如此,还是有很多理由支持/反对函数式编程,上面重点介绍了根据您的示例使用(或不使用)函数式接口的原因(特别是从编写合同的开发人员的角度)这似乎是一个非常常见的问题,尽管在我看来答案很简单。
对我来说,这是关于你如何看待你设计的合同,以及你期望它如何实施和使用
正如您所注意到的,您的示例显示了一种使用函数接口的糟糕方式。设计这些类型只是为了调用if(dt.test(d))
,而不是if(d.isemamifero())
使用了一个简单的形容词:bad。这可能要归咎于教科书。问题是大多数书籍教我们如何使用函数接口,就像我们通常被教如何使用接口/抽象一样,而这忽略了函数编程的要点和大局。当然,我们需要知道“如何”使用实现一个功能接口(毕竟它是一个接口),但是很多书没有告诉我们在哪里应用它们
下面是我如何向自己解释的(用非常基本的术语):
1-将功能接口视为“命名逻辑”(在合同另一方实施)
是的,功能接口是一种类型,但将功能接口命名为逻辑更为合理。与普通类型(如可序列化
,集合
,自动关闭
)不同,功能接口(如测试仪
(或谓词
)表示逻辑(或者仅仅是代码)。我知道这些细微差别正在变得越来越微妙,但我相信传统的OOP抽象“类型”和功能接口的含义是有区别的
2-将实现功能接口的代码与使用它的代码隔离开来
在同一个组件中使用这两个函数的问题在您的代码中显而易见。您不会编写一个函数接口,声明一个函数接口的方法,所有这些都只是为了实现它并传递i
Collection<String> strings = Arrays.asList("a", "b", "c");
strings.forEach(s -> System.out.println(s));
public static void pintadorDinosaurios(List<Dinosaurio> ld, DinosaurioTester dt) {
ld.stream().filter(dt::test).foreach(System.out::println);
}
pintadorDinosaurios(lista, new DinosaurioTester {
boolean test(Dinosaurio d) {
return d.isEsMamifero();
}
});