When passing methods as parameters, Ruby has proc, C # has Delegate, and JavaScript, let alone Java, is embarrassed in this respect. But Java 8 provides Lambda expression and function interfaces, which is undoubtedly the gospel of Javer and makes Java a better language for excellence and ease of use.
demand
Team C is an agile development team whose products are released dozens of times a day in small versions, so frequent and continuous integration must be guaranteed by good code. For this reason, PM has set a "threshold" in the construction task of CI, and all deployments must scan Sonar code to meet certain threshold before normal release, so as to ensure product quality. Specific indicators are as follows:
- 1. The test coverage of the project must be greater than 90%.
Analysis
Obtain all projects, screen out projects with test coverage greater than 90%, and publish them so easy.~
Realization
private static List<Project> getValidProjects(List<Project> projectList) {
List<Project> validProjects = new ArrayList<>();
for(Project project : projectList) {
if(project.getTestCoverage() > 0.9) {
validProjects.add(project);
}
}
return validProjects;
}
The code is simple. It was implemented before Java 8. Of course, we can also abstract project. getTestCoverage ()> 0.9 into project.isValid(), which seems to be no big problem.
Demand 2 is coming. The new indicators of PM are as follows:
- 1. The test coverage of the project must be greater than 90%.
- 2. The number of code smells should not exceed three
Similarly easy, you quickly abstracted in Project.java:
public boolean isValid() {
return this.getTestCoverage() > 0.9 && this.getCodeSmellNum() < 3;
}
There are always problems when deployable. PM improves the quality threshold again. The indicators are as follows:
- 1. The test coverage of the project must be greater than 90%.
- 2. The number of code smells should not exceed three
- 3. Scanning problems should not exceed 3
- 4. The number of API test cases is more than 10
- 5. The number of UI test cases is greater than 10
- …
- …
- …
At this time, you do not want to kill PM, but you have no choice, or you have to add N-judgment on this basis.
Java 8 is coming...
Predicate
After careful analysis of the current requirements, the method we want to implement is the result of "some behavior" to guide the set of checks, which means that the above method parameters should be similar:
private static List<Project> getValidProjects(List<Project> projectList, Validation validation) {
List<Project> validProjects = new ArrayList<>();
for(Project project : projectList) {
if(validation.test()) {
validProjects.add(project);
}
}
return validProjects;
}
All cases are unified and abstracted into Validation interface. The test method in the interface represents the result of verification. If this is done, it will become polymorphic, of course, can be achieved, but the design is more complex: a good screening, to write an interface and then write N classes to achieve, it is not very bad. Java 8 implements function interfaces on this basis. Validation can be regarded as a method that has not yet been implemented. Correspondingly, we can write as follows:
private static List<Project> getValidProjects(List<Project> projectList, Predicate<Project> p) {
List<Project> validProjects = new ArrayList<>();
for(Project project : projectList) {
if(p.test(project)) {
validProjects.add(project);
}
}
return validProjects;
}
List<Project> validProjects = getValidProjects(projectList, project-> project.getTestCoverage() > 0.9);
It looks cool to pass project - > project. getTestCoverage () > 0.9 as a method parameter. Correspondingly, we can aggregate the various predicate s above:
Predicate<Project> testCoveragePredicate = project-> project.getTestCoverage() > 0.9;
Predicate<Project> codeSmellPredicate = project-> project.getCodeSmellNum() < 3;
Predicate<Project> issuePredicate = project-> project.getIssueCount() < 3;
Predicate<Project> apiPredicate = project-> project.getApiCount() > 10;
Predicate<Project> uiPredicate = project-> project.getApiCount() > 10;
Predicate<Project> projectPredicate = testCoveragePredicate
.and(codeSmellPredicate)
.and(codeSmellPredicate)
.and(issuePredicate)
.and(apiPredicate)
.and(uiPredicate);
List<Project> validProjects = getValidProjects(projectList, projectPredicate);
This kind of code, since it's beautiful, expressive and maintainable, is cool. But it's not hard to see that Predicate is in the form of T - > boolean. What if other types are needed? Here we briefly introduce the other two function interfaces:
Consumer and Function
Consumer, Function is similar to Predicate except that the former is in the form of T - > void, while the latter is in the form of T - > R, and the implementation method is slightly different.
Demo for Consumer:
private static void displyAllCodeSmell(List<Project> projectList, Consumer<Project> c) {
for(Project project : projectList) {
c.accept(project);
}
}
displyAllCodeSmell(projectList, project -> System.out.println(project.getCodeSmellNum()));
Demo for Function:
private static int calcAllIssueCount(List<Project> projectList, Function<Project, Integer> f) {
int sum = 0;
for(Project project : projectList) {
sum += f.apply(project);
}
return sum;
}
calcAllIssueCount(projectList, project -> project.getIssueCount());
Other function interfaces
Interface | Description | |
---|---|---|
Predicate | T->boolean | |
Consumer | T->void | |
Function | T->R | |
Supplier | ()->T | |
UnaryOperator | T->T | |
BinaryOperator | (T,T)->T | |
BiPredicate | (L,R)->boolean | |
BiConsumer | (T,U)->void | |
BiFunction(T,U,R) | (T,U)->R |
2Not signed i