Gen*_*aut 1 java lambda functional-programming java-8
我正在阅读有关函数式编程的基础知识.无论如何,我做了一些例子试图理解这个概念并正确使用它.
我没有掌握这种函数式编程的强大功能.它只是在编写lambda而不是普通代码吗?
如果我们有这个课程:
public class Dinosaurio {
private boolean esMamifero;
private String nombre;
public Dinosaurio(String n, boolean esMam) {...}
//getters and setters
Run Code Online (Sandbox Code Playgroud)
这个功能界面:
@FunctionalInterface
public interface DinosaurioTester {
boolean test(Dinosaurio d);
}
Run Code Online (Sandbox Code Playgroud)
而这个主要课程:
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());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这很好用,但我没有看到使用它的真正优势:
if(dt.test(d)) {
Run Code Online (Sandbox Code Playgroud)
而不是这个:
if(d.isEsMamifero())
Run Code Online (Sandbox Code Playgroud)
编辑:当然这个例子是功能编程实际使用的错误示例代码.但这就是我现在能想到的一切.
这似乎是一个非常普遍的问题,尽管在我看来它有一个非常简单的答案.
对我来说,这是关于你如何看待你设计的合同以及你希望如何实现和使用它.
如您所知,您的示例显示了使用功能接口的不良方式.设计这些类型只是为了结束调用if(dt.test(d))而不是if(d.isEsMamifero())一个简单的形容词:坏.这可能与教科书有关.问题是大多数书籍教会我们使用功能接口,就像我们被教导一般使用接口/抽象一样,这省略了函数式编程的重点和更大的图景.当然,人们需要知道如何实现功能界面(毕竟它是一个界面),但许多书籍并没有告诉我们应用它们的位置.
以下是我自己解释的内容(用非常基本的术语):
1 - 将功能界面视为"命名逻辑"(将在合同的另一侧实施)
是的,功能接口是一种类型,但将功能接口视为命名逻辑更有意义.不同于普通的类型,如Serializable,Collection,AutoCloseable,功能intefaces像Tester(或Predicate)表示逻辑(或只是代码).我知道细微差别正在变得微妙,但我相信传统的OOP抽象"类型"与功能界面的意义之间存在差异.
2 - 将实现功能接口的代码与使用它的代码隔离开来
在代码中显而易见的是在同一组件中使用这两个问题.你不会编写一个函数接口,声明一个接受一个的方法,只是为了实现它并将它传递给你自己的方法.如果您正在执行此操作并且仅此操作,那么您出于错误的原因使用抽象,更不用说正确使用功能接口了.
有大量正确使用功能接口的例子.我来接Collection.forEach有Consumer:
Collection<String> strings = Arrays.asList("a", "b", "c");
strings.forEach(s -> System.out.println(s));
Run Code Online (Sandbox Code Playgroud)
这与您的设计有何不同?
Collection.forEach合同中的停止设计者采用a Consumer(他们不使用Consumerjust来命名/键入参数到他们自己的方法Collection.forEach是一个需要定制逻辑的操作.正如s -> System.out.println(s),s -> myList.add(s)s -> myList.add(s.toUpperCase()),Collection接口的forEach方法编排迭代,并允许呼叫方提供在每次迭代调用的逻辑.这引起了人们的关注.Stream.filter与Predicate您的示例更接近,将您的使用方式Stream.filter与您Tester.test在示例中的使用方式进行对比将是一个好主意.
有了这个说法,有很多理由支持/反对函数式编程,上面的重点是根据你的例子使用(或不使用)函数接口的原因(具体来说,从编写合同的开发人员的角度来看).
您混淆了两个术语:函数式接口和函数式编程。Java 中引入了函数式接口作为一种类型化语言,以实现函数式编程。没有好的方法来声明匿名方法(你必须使用匿名类。并且每个匿名类都定义一个新类,当你使用许多匿名方法时,可能会导致类污染。)但是,匿名方法是函数式的一个非常重要的元素。编程。因此,如果您想到函数式接口的强大功能,您更应该考虑 Java 的 Stream API。它使您能够以声明式风格表达代码,在这种风格中您可以定义您想要的内容,而不是如何获取它。如果你只想用 替换if(d.isEsMamifero()),if(dt.test(d))那么你赢的不多。但那又怎么样:
public static void pintadorDinosaurios(List<Dinosaurio> ld, DinosaurioTester dt) {
ld.stream().filter(dt::test).foreach(System.out::println);
}
Run Code Online (Sandbox Code Playgroud)
现在您已通过界面参数化了过滤条件。该表达式没有说明您如何过滤。它只说你想要一种恐龙并将它们打印到控制台。表达式是否使用循环或递归是完全隐藏的。您可以通过 致电 pintadorDinosaurios pintadorDinosaurios(lista, a->a.isEsMamifero());。
如果没有函数式接口,则必须编写
pintadorDinosaurios(lista, new DinosaurioTester {
boolean test(Dinosaurio d) {
return d.isEsMamifero();
}
});
Run Code Online (Sandbox Code Playgroud)
看起来很丑。因此,函数式接口与 Stream API 的结合使您能够以更舒适的方式编写更短的代码(如果您习惯的话)。
| 归档时间: |
|
| 查看次数: |
102 次 |
| 最近记录: |