The idea for this post came from the desire to help others understand the basics of lambdas and interfaces. I was surprised to find that I didn’t know a lot of things about them, and it was challenging to find good articles explaining them. So, I decided to write my own. Ecs. I’ve been asked a lot of questions about learning interfaces and lambda expressions, and one of the more common ones I hear is, “What’s the point of learning interfaces when I can always code in C/C++ or Java?”
Java interfaces
In Java, an interface is a contract for a class. The contract described in the interface defines the methods to be implemented in the class. Classes that implement the contract must either execute the entire contract or be declared as an abstract. Java interfaces are used to define abstract types. Interfaces :
- Similar to abstract classes containing only public abstract methods
- Specify the methods to be implemented by the class – methods may not have an implementation in parentheses.
- Can contain permanent fields
- Can be used as a reference type
- are an important part of many building models
Problem solved by interfaces
A company sells a range of very different products and needs a way to access financial data in a similar way. Products include:
- Broken stone (measured in pounds)
- Red color (measured in gallons)
- Widgets (measured in number)
It is necessary for each product to calculate : How can interfaces solve this problem? All these products are sold by the same company, but measured differently.
Broken stone class
CrushedRock class before interfaces : public class CrushedRock { private String name ; private double salesPrice = 0 ; private double cost = 0 ; private double weight = 0 ; // In pounds public CrushedRock(double sales price, double cost, double weight) { this.salesPrice = sales price; this.cost = cost; this.weight = weight; } } Note that this class has a weight field. However, the RedPaint class has a gallon field and the Widget class has a quantity field. The calculation of the data requirements for the three classes is similar, but differs for each class.
SalesCalcs interface
The SalesCalcs interface defines the methods to be implemented by the class. The signature of the method determines what is passed and what is returned.
Rules for interfaces
- Access modifiers : All methods of the interface are public, even if you forgot to declare them as such. You cannot declare methods as private or protected in an interface.
- Summary Modifier: Since all methods are implicitly abstract, it is redundant (but acceptable) to declare a method as abstract. Since all methods in the interface are abstract, you can’t provide any method implementations, not even an empty set of parentheses.
- Implementation of multiple interfaces : A class can implement more than one interface in a comma-separated list at the end of the class declaration.
The SalesCalcs interface defines the types of calculations required for our products. High-level public interfaces are declared in their own .java file. public interface SalesCalcs { public String getName(); public double calcSalesPrice(); public double calcCost(); public double calcProfit(); }
Add interface
The updated CrushedRock class implements SalesCalcs. public class CrushedRock implements SalesCalcs{ private String name = Crushed Rock; … // line not displayed @Override public double calcCost(){ return this.cost * this.weight; } @Override public double calcProfit(){ return this.calcSalesPrice() – this.calcCost(); } } In the declaration line of the class, the keyword implements defines the interface SalesCalcs for this class. Each of the methods specified in SalesCalcs must be implemented. However, the way these methods are applied may differ from class to class. The only requirement is that the signature of the method matches. This means that the cost price or the selling price can be calculated differently in different classes.
Interface connections
Any class that implements an interface can be referenced by that interface. Note how the calcSalesPrice method can be accessed by the CrushedRock class or the SalesCalcs interface: CrushedRock rock1 = new CrushedRock(12, 10, 50); SalesCalcs rock2 = new CrushedRock(12, 10, 50); System.out.println(Salesprice : + rock1.calcSalesPrice()); System.out.println(Salesprice : + rock2.calcSalesPrice()) Exit: Sale Price : 600.0 Sales price : 600.0 Since CrushedRock implements SalesCalcs, the SalesCalcs reference can be used to access data from the CrushedRock object.
Utility of the interface connection
Any class that implements an interface can be referenced by that interface. For example: SalesCalcs[] itemList = new SalesCalcs[5] ; ItemReport report = new ItemReport() ; itemList[0] = new CrushedRock(12.0, 10.0, 50.0); itemList[1] = new CrushedRock(8.0, 6.0, 10.0); itemList[2] = new RedPaint(10.0, 8.0, 25.0); itemList[3] = new Widget(6.0, 5.0, 10); itemList[4] = new Widget(14.0, 12.0, 20) ; System.out.println(==SalesCalcs item==); for(SalesCalcs item:itemList){ report.printItemData(item); } Since these three classes share a common interface, a list of different classes like the one above can be created and processed in the same way.
Interface code flexibility
An auxiliary class that references an interface can handle any implementation class. public class ItemReport {public void printItemData(SalesCalcs item){System.out.println(– + item.getName() + Report–);System.out.println(SalesPrice: +item calcSalesPrice();System.out.println(SalesPrice: +item.calcSalesPrice());System.out.println(Cost: +item.calcCost());System.out.println(Profit: +item.calcProfit());}} Instead of writing a method to print the data for each class, you can use an interface reference to retrieve the data for all three classes.
Standard methods in interfaces
Java 8 adds default methods as a new feature. By using the default keyword, you can make fully implemented methods available to all implementing classes. The following example shows how the material report, previously implemented as a separate class, can be fully implemented as a standard method. Now all three classes automatically get the fully implemented printItemReport method. public interface SalesCalcs { … // omitted line public default void printItemReport(){ System.out.println(– + this.getName() + Report–); System.out.println(SalesPrice: + this.calcSalesPrice()); System.out.println(Cost: + this.calcCost()); System.out.println(Profit: + this.calcProfit()); } } This feature was added to simplify the development of APIs that rely heavily on interfaces. Previously, simply adding a new method would break all implementation and extension classes. Default methods can now be added or changed without affecting the API hierarchy. Standard methods :
- Declare with the standard keyword
- are methods that are fully implemented in the
- Ensuring useful inheritance mechanisms
Standard method: Example
Here is an updated version of the article’s report using standard methods. SalesCalcs[] itemList = new SalesCalcs[5] ; itemList[0] = new CrushedRock(12, 10, 50); itemList[1] = new CrushedRock(8, 6, 10); itemList[2] = new RedPaint(10, 8, 25); itemList[3] = new Widget(6, 5, 10); itemList[4] = new Widget(14, 12, 20) ; System.out.println(==SalesCalcs item==); for(SalesCalcs item:itemList){ item.printItemReport(); } Printing a report is now as simple as calling the printItemReport method.
Static methods in interfaces
In Java 8 you can use static methods in an interface. Therefore, you can make auxiliary methods like the following. public interface SalesCalcs { … // An array of rows is omittedpublic static void printItemArray(SalesCalcs[] items){System.out.println(reportTitle);for(SalesCalcs item:items){System.out.println(– + item.getName() + report–);System.out.println(SalesPrice: + item.calcSalesPrice());System.out.println(Cost: + item.calcCost());System.out.println(Profit: + item.calcProfit());} This is a practical function. You can now include the helper method in an interface instead of a separate class as described above. Here is an example of a method call: SalesCalcs.printItemArray(itemList) ;
Constant fields
Permanent fields are allowed in the interface. When you declare a field in an interface, it is implicitly public, static and finite. You can specify these modifiers redundantly. public interface SalesCalcs { public static final String reportTitle=n==Statistical list report==; … // Some lines are missing
Interface extension
Interfaces can extend interfaces: public interface WidgetSalesCalcs extends SalesCalcs{ public String getWidgetType(); } So, any class that implements WidgetSalesCalc must now implement all methods of SalesCalcs, in addition to the new method specified here.
Implementation and expansion
Classes can extend the parent class and create a : public class WidgetPro extends Widget implements WidgetSalesCalcs{ private String type ; public WidgetPro(double selling price, double cost, long quantity, string type){ super(selling price, cost, quantity); this.type = type; } public String getWidgetType(){ return type; } }
Extends the first
If you are using both extensions and implementations, the extensions should come first.
Anonymous internal classes
Define the class locally and not in a separate file. Why are you doing this?
- Logical grouping of code in one place
- Increased encapsulation
- Making the code more readable
StringAnalyzer interface: public interface StringAnalyzer { public boolean analyze(String target, String searchStr); } Such an interface with only one method is called a functional interface. Accepts two strings and returns a boolean value.
Anonymous indoor class: Example
Example of a method call with a specific class 20 // Call the specific class involving StringAnalyzer 21 ContainsAnalyzer contains = new ContainsAnalyzer(); 22 23 System.out.println(==contains==); 24 Z03Analyzer.searchArr(strList01, searchStr, contains) ; An example of an anonymous inner class: 22 Z04Analyzer.searchArr(strList01, searchStr, 23 new StringAnalyzer(){ 24 @Override 25 public boolean analyze(String target, String searchStr){ 26 return target.contains(searchStr); 27 } 28 }) The class is created on the spot. This example shows how an anonymous inner class can be replaced by an object. Here is the source code for ContainsAnalyzer : public class ContainsAnalyzer implements StringAnalyzer { public boolean analyze(String target, String searchStr){ return target.contains(searchStr); } } Note that the anonymous inner class has no name, but implements almost exactly the same code. The syntax is a bit complicated because the class is defined where the parameter variable normally is.
String analysis Regular class
The class parses the string array specified by the search string. It produces strings containing the search string. You can write other methods to perform similar string checks. Regular classroom sample method : 1 package com.example; 2 3 public class AnalyzerTool { 4 public boolean arrContains(String sourceStr, String searchStr){ 5 return sourceStr.contains(searchStr); 6 } 7 } 8 The method takes a source string and searches for text that matches the text in the search string. If there is a match, the true message is returned. If no match is found, false is returned. Additional methods can be written to compare lengths or to determine if a string starts with a search string. For the sake of simplicity, only one method is used.
String analysis Regular test class
Here is the code to test the Z01Analyzer class: 4 public static void main(String[] args) { 5 String[] strList = 6 {morning,too,to,timbukto,the,hello,heat}; 7 String searchStr = to; 8 System.out.println(search for : + searchStr); 9 10 // Create a regular class 11 AnalyzerTool analyzeTool = new AnalyzerTool(); 12 13 System.out.println(===Contains==); 14 for(String currentStr:strList){ 15 if (analyzeTool.arrContains(currentStr, searchStr)){ 16 System.out.println(Hits: + currentStr); 17 } 18 } 19} First class is pretty standard. The test array is passed to the foreach loop, where the method is used to produce the corresponding words. Here’s the result: ==Content== Assent: tomorrow Assent: toto Assent: to Assent: timbukto
Character string interface: Example
What about using the interface? The example uses an interface instead of a normal class. Note that StringAnalyzer is a functional interface because it has only one method. Other than the addition of a paragraph, the course has not changed. 3 public interface StringAnalyzer { 4 public boolean analyze(String sourceStr, String searchStr); 5 } StringAnalyzer is a single method functional interface. The replacement of the previous example and the implementation of the interface are as follows: 3 public class ContainsAnalyzer implements StringAnalyzer { 4 @Override 5 public boolean analyze(String target, String searchStr){ 6 return target.contains(searchStr); 7 } 8 }
Channel analyser interface test class
Switching to an interface does not change this test class much. The only difference is that a different class is used to test strings. If additional tests are to be run, additional foreach loops are required and a separate class for each test condition. That’s probably a step backwards. However, this approach also has its advantages. 4 public static void main(String[] args) { 5 String[] strList = 6 {tomorrow,too,to,timbukto,the,hello,heat}; 7 String searchStr = to; 8 System.out.println(Search for: + searchStr); 9 10 // Call the specific class that involves the StringAnalyzer 11 ContainsAnalyzer contains = new ContainsAnalyzer(); 12 13 System.===Contains==); 14 for(String currentStr:strList){ 15 if (contains.analyze(currentStr, searchStr)){ 16 System.out.println(match: + currentStr); 17 } 18 } 19 }
Barrel encapsulation for barrel
By encapsulating the for loop in a static helper method, only one loop is needed to process each type of string tested with the StringAnalyzer interface. The method searchArr remains unchanged in all the following examples. 3 public class Z03Analyzer { 4 5 public static void searchArr(String[] strList, String searchStr, StringAnalyzer analyzer){ 6 for(String currentStr:strList){ 7 if (analyzer.analyze(currentStr, searchStr)){ 8 System.out.println(Match : + currentStr); 9 } 10 } 11 } // Set of missing rules Note the parameters of the method: 1. String array 2. Find string 3. The class that implements the StringAnalyzer interface
Testing the string analysis class with auxiliary method
With the auxiliary method, the main method reduces to this: 13 public static void main(String[] args) { 14 String[] strList01 = 15 {tomorrow,too,to,timbukto,the,hello,heat}; 16 String searchStr = to; 17 System.out.println(Wanted : + searchStr); 18 19 // Call the specific class that involves the StringAnalyzer 20 ContainsAnalyzer contains = new ContainsAnalyzer(); 21 22 System.out.println(===Contains==); 23 Z03Analyzer.searchArr(strList01, searchStr, contains); 24 } Now you can search the table and display the results with a single call to line 23.
String analysis Anonymous internal class
Create an anonymous inner class for the third argument. 19 // Implementing an anonymous inner class 20 System.out.println(===Contains==); 21 Z04Analyzer.searchArr(strList01, searchStr, 22 new StringAnalyzer(){ 23 @Override 24 public boolean analyze(String target, String searchStr){ 25 return target.contains(searchStr); 26 } 27 }); 28 } In this example, the third argument of the method call is an anonymous inner class. Note that the structure of the class is the same as that of ContainsAnalyzer in the previous example. In this approach, the code is stored in the calling class. Moreover, the logic of the analysis method can be easily adapted to the circumstances. However, there are some drawbacks. 1. The syntax is a bit complicated. The full definition of the class is in parentheses in the argument list. 2. Since there is no class name, when the code is compiled, a class file is created and a class number is assigned to the class. This is not a problem if there is only one anonymous inner class. However, when there are multiple classes, it is difficult to determine which class file corresponds to which source file.
Lambda chain analysis
Use a lambda expression for the third argument. 13 public static void main(String[] args) { 14 String[] strList = 15 {tomorrow,too,to,timbukto,the,hello,heat}; 16 String searchStr = to; 17 System.out.println(Wanted : + searchStr); 18 19 // Lambda expression replaces an anonymous inner class 20 System.out.println(==Contains==); 21 Z05Analyzer.searchArr(strList, searchStr, 22 (String target, String search) -> target.contains(search)); 23 } In Java 8, a lambda expression can be replaced by an anonymous inner class. Remember some important facts.
- The lambda expression has two arguments: As in the previous two examples, the same arguments are used in the lambda expression as in the analysis method.
- The lambda expression returns a boolean value: As in the previous two examples, a boolean value is returned, as in the method analyze.
In fact, a lambda expression, an anonymous inner class and a concrete class are essentially equivalent. A lambda expression is a new way of expressing the logic of the code, using a functional interface as a basis.
Lambda expression defined
A lambda expression consists of a list of arguments, an arrow mark, and a block or expression. When you use a block code, you can include several statements. Parameter types can be specified or output.
List of arguments | Tree token | Body |
(int x, int y) | -> | x+y |
Basic examples of lambda : (int x, int y) -> x + y (x, y) -> x + y (x, y) -> { system.out.println(x + y);} (String s) -> s.contains(word) s -> s.contains(word)
What is a lambda expression?
Compare the lambda expression with the implementation of the StringAnalyzer interface. ContainsAnalyzer.java : public class ContainsAnalyzer implements StringAnalyzer { public boolean analyze(String target, String searchStr){ return target.contains(searchStr); } } The figure above compares the lambda expression with the implementation of the StringAnalyzer interface. They are essentially equivalent, since a lambda expression can be replaced by an anonymous inner class or an implementation class. All three cases rely on the StringAnalyzer interface as the underlying mechanism. The figure above compares the lambda expression with the implementation of the StringAnalyzer interface. They are essentially equivalent, since a lambda expression can be replaced by an anonymous inner class or an implementation class. All three cases rely on the StringAnalyzer interface as the underlying mechanism.
Decreased expression of lambda
Lambda expressions with shortened syntax : 20 // Use the lambda short form 21 System.out.println(==Contains==); 22 Z06Analyzer.searchArr(strList01, searchStr, 23 (t, s) -> t.Contains(s)); 24 25 // It becomes easy to change the logic 26 System.out.println(==Starts with==); 27 Z06Analyzer.searchArr(strList01, searchStr, 28 (t, s) -> t.starts with(s)) The arguments of the SearchArr method are : public static void searchArr(String[] strList, String searchStr, StringAnalyzer analyzer) The first example is similar to the previous code example. The only difference is the use of an abbreviation for lambda expressions. The type of arguments is determined by the context in which the lambda expression is used. So the compiler knows that the signature of StringAnalyzer.analyze is : public boolean analyze(String sourceStr, String searchStr) ; So, two strings are passed and a boolean value is returned. Note the second example on line 28. Now changing the logic into a functional interface becomes trivial.
Lambda expressions as variables
There is a certain type of person who loves to write, and that’s the type of person who wants to write about their journey through life. This person is usually a geek, and loves to have a straightforward approach to life. This person is the nerd of the world, and is usually seen with a notebook in her hands and notepad on her lap. This person is the one that loves to read books and articles about their favorite things, they are the ones that have a vast knowledge of their field, and the ones that also want to share that knowledge with all their readers.