Lambda Expression in Java: Power of Functional Programming


Introduction to Lambda Expressions

Introduction to Java 8 Lambda Expressions:

Lambda Expression in Java are a powerful feature introduced in Java 8, revolutionizing the way developers write code by enabling functional programming paradigms. They provide a concise and expressive way to represent anonymous functions, allowing for cleaner and more readable code.

Overview of Lambda Expressions:
Lambda Expression in Java serve as a shorthand notation for defining small, inline functions without the need for explicit method definitions or anonymous inner classes. They encapsulate behavior and promote functional programming concepts, such as higher-order functions and deferred execution.

Syntax and Structure:
The syntax of a Lambda Expression in Java consists of parameters, an arrow token (->), and a body. Parameters represent the input to the lambda expression, and the arrow token separates the parameters from the body. The body contains the code that implements the functionality of the lambda expression.

Functional Interfaces:
Lambda Expression in Java are closely tied to functional interfaces, which are interfaces containing a single abstract method. They serve as the target type for lambda expressions, allowing them to be assigned to variables or passed as arguments to methods. Java 8 provides a set of predefined functional interfaces in the java.util.function package, such as Consumer, Predicate, and Function, which cover common use cases for lambda expressions.

In summary, Lambda Expression in Java offer a concise and expressive way to represent functionality, leveraging functional programming principles to enhance code readability and maintainability. They are closely associated with functional interfaces, providing a flexible and powerful mechanism for implementing behavior in Java applications.

Functional Interfaces

Functional interfaces play a pivotal role in Java 8’s support for lambda expressions and functional programming. They are interfaces that contain exactly one abstract method, also known as the functional method. These interfaces can have any number of default or static methods, but they must have only one abstract method.

Definition and Role of Functional Interfaces

Functional interfaces serve as blueprints for lambda expressions and method references. They provide a contract specifying the signature of the lambda expression or method reference that can be used to implement the functional method. By enforcing single abstract method restriction, functional interfaces enable lambda expressions to be used wherever instances of the interface are expected, facilitating a more concise and expressive coding style.

Predefined Functional Interfaces in Java 8

Java 8 introduces a rich collection of predefined functional interfaces in the java.util.function package, covering a wide range of common use cases for lambda expressions. Some of the commonly used predefined functional interfaces include:

  • Consumer<T>: Represents an operation that takes a single input argument and returns no result.
  • Supplier<T>: Represents a supplier of results, producing instances of type T.
  • Function<T, R>: Represents a function that takes an argument of type T and produces a result of type R.
  • Predicate<T>: Represents a predicate (boolean-valued function) of one argument.

These predefined functional interfaces provide ready-made solutions for common functional programming scenarios, enabling developers to leverage lambda expressions effectively.

Creating Custom Functional Interfaces

In addition to predefined functional interfaces, developers can create their custom functional interfaces to model specific functional requirements in their applications. To create a custom functional interface, simply declare an interface with a single abstract method. Annotating the interface with the @FunctionalInterface annotation is optional but recommended to ensure that the interface meets the criteria of a functional interface.

Custom functional interfaces allow developers to define domain-specific functionality and provide a clear contract for lambda expressions and method references that implement the interface’s functional method. They enhance code readability, maintainability, and reusability by encapsulating behavior in a well-defined interface.

In summary, functional interfaces serve as the cornerstone of Java 8’s support for lambda expressions and functional programming, enabling developers to write more expressive and concise code. They provide a standardized approach to modeling behavior and are instrumental in leveraging the power of lambda expressions in Java applications.

Lambda Expression Syntax

Lambda Expression in Java provide a concise way to represent anonymous functions. They are particularly useful for implementing functional interfaces with a single abstract method. The syntax of a lambda expression consists of three main components: the parameter list, the arrow operator, and the body.

Basic Syntax of Lambda Expression in Java:
A lambda expression follows the basic syntax:

(parameters) -> expression

or

(parameters) -> { statements; }

Parameter List and Arrow Operator:

  • Parameters: The parameter list contains zero or more parameters enclosed in parentheses. If there is only one parameter, the parentheses can be omitted. If there are no parameters, empty parentheses are used.
  • Arrow Operator (->): The arrow operator separates the parameter list from the body of the lambda expression. It is used to indicate that the parameters are being passed to the lambda body.

Body of Lambda Expression in Java:

  • Expression Body: If the lambda body consists of a single expression, it can be provided directly after the arrow operator. The result of the expression will be the return value of the lambda function.
  • Block Body: If the lambda body consists of multiple statements, they must be enclosed in curly braces {}. In this case, explicit return statements are required if the return value needs to be specified.

Here’s an example of a lambda expression that takes two integers as parameters and returns their sum:

(int a, int b) -> a + b

And here’s an example of a lambda expression with a block body that calculates the factorial of a number:

(n) -> {
    int result = 1;
    for (int i = 1; i <= n; i++) {
        result *= i;
    }
    return result;
}

Visual Representation of Lambda Expression in Java

With method arguments

java 8 lambda function

In summary, Lambda Expression in Java provide a compact and expressive syntax for implementing functional interfaces. They consist of a parameter list, an arrow operator, and a body, which can be either a single expression or a block of statements. Lambda expressions enable the creation of inline functions, making code more readable and concise.

Using Lambda Expression in Java

Lambda Expression in Java provide a powerful mechanism for writing concise and expressive code. They can be used in various contexts, including passing them as arguments to methods, assigning them to variables, and using them with functional interfaces.

Passing Lambda Expressions as Arguments

One common use of lambda expressions is passing them as arguments to methods. This allows for the implementation of behavior to be defined inline, making code more modular and flexible. For example:

// Method that takes a lambda expression as an argument
public void process(Consumer<Integer> action) {
    action.accept(10);
}

// Calling the method with a lambda expression
process((x) -> System.out.println("Number: " + x));

Assigning Lambda Expressions to Variables

Lambda Expression in Java can also be assigned to variables, which allows for reusing the same behavior in multiple places in the code. This makes the code more readable and reduces duplication. For example:

// Assigning a lambda expression to a variable
Consumer<Integer> action = (x) -> System.out.println("Number: " + x);

// Using the variable
action.accept(10);

Using Lambda Expressions with Functional Interfaces

Lambda Expression in Java are closely associated with functional interfaces, which serve as the target type for lambda expressions. Functional interfaces provide a contract specifying the signature of the lambda expression or method reference that can be used to implement the functional method. For example:

// Functional interface with a single abstract method
interface MyFunction {
    void apply(int x);
}

// Using lambda expression to implement the functional method
MyFunction function = (x) -> System.out.println("Number: " + x);

// Invoking the method
function.apply(10);

In summary, lambda expressions in Java 8 can be passed as arguments to methods, assigned to variables, and used with functional interfaces, providing a flexible and powerful mechanism for defining behavior inline. They promote a more functional programming style, leading to cleaner and more expressive code.

Examples and Use Cases

Examples and Use Cases of Java 8 Lambda Functions:

Lambda expressions in Java 8 offer a concise and expressive way to implement functional programming concepts. They are particularly useful in scenarios involving collections, streams, and other functional interfaces. Here are some examples and use cases:

Simple Examples of Lambda Expressions

  1. Runnable Example:
// Using lambda expression to create a Runnable
Runnable runnable = () -> System.out.println("Hello, world!");

// Running the Runnable
runnable.run();
  1. Comparator Example:
// Sorting a list of strings using a lambda expression for comparator
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, (a, b) -> a.compareTo(b));

Use Cases in Collections and Streams

  1. Filtering Elements:
// Filtering even numbers using a lambda expression in a stream
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
                                   .filter(n -> n % 2 == 0)
                                   .collect(Collectors.toList());
  1. Mapping Elements:
// Mapping names to uppercase using a lambda expression in a stream
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> upperCaseNames = names.stream()
                                  .map(String::toUpperCase)
                                  .collect(Collectors.toList());

Functional Programming Concepts

  1. Higher-Order Functions:
    Lambda expressions enable the use of higher-order functions, functions that take other functions as arguments or return functions as results. This promotes composability and modularity in code.
  2. Lazy Evaluation:
    Functional interfaces and streams in Java 8 support lazy evaluation, where operations are only performed when needed. This allows for efficient processing of large data sets.
  3. Immutability:
    Functional programming encourages immutability, where objects cannot be modified after creation. Lambda expressions facilitate the creation of immutable objects and promote safer concurrency.

In summary, Java 8 lambda functions provide a powerful tool for implementing functional programming concepts in Java applications. They enable concise and expressive code, promote modularity and composability, and support advanced features such as lazy evaluation and immutability.

Lambda Expressions vs. Anonymous Inner Classes

Differences between lambda expressions and anonymous inner classes

Here’s a comparison between Lambda Expressions and Anonymous Inner Classes in Java.

AspectLambda ExpressionsAnonymous Inner Classes
SyntaxConcise syntax.Bulky syntax.
ReadabilityImproves code readability.Can make code less readable due to verbosity.
Type InferenceInfers types automatically.Requires explicit type declarations.
Scope of VariablesCan access effectively final and effectively final variables from the enclosing scope.Can access variables from the enclosing scope, but those variables must be declared as final or effectively final.
SerializationNot serializable.Serializable, as they are named classes.
PerformanceGenerally more efficient due to reduced overhead.May have slightly more overhead due to class instantiation.
InstantiationUses invokedynamic bytecode instruction.Creates a new class instance.
CompilationMore optimized by the Java compiler.May result in more bytecode and slower execution.
Use CasesPreferred for concise, functional-style programming.Suitable for more complex scenarios where additional control or behavior is needed.
Contextual TypingTakes advantage of type inference.Requires explicit type annotations.
CompatibilityIntroduced in Java 8.Available in earlier versions of Java.
Lambda Expressions vs. Anonymous Inner Classes

In summary, Lambda Expressions offer a more concise and expressive syntax for implementing functional interfaces, while Anonymous Inner Classes provide additional control and behavior but with more verbosity. Lambda Expressions are generally preferred for functional-style programming, whereas Anonymous Inner Classes are suitable for more complex scenarios where fine-grained control is needed.

Lambda Expression Advantages and disadvantages of each approach

Here’s a breakdown of the advantages and disadvantages of Lambda Expressions and Anonymous Inner Classes in Java:

Lambda Expressions:

Advantages:

  1. Concise Syntax: Lambda expressions offer a more concise syntax compared to anonymous inner classes, resulting in cleaner and more readable code.
  2. Improved Readability: They improve code readability by eliminating boilerplate code, making it easier to focus on the core logic.
  3. Type Inference: Lambda expressions can infer types automatically, reducing the need for explicit type declarations and making code more flexible.
  4. Performance: They are generally more efficient due to reduced overhead, as they use invokedynamic bytecode instruction.
  5. Functional Programming: Lambda expressions promote a functional programming style, enabling developers to write more expressive and modular code.

Disadvantages:

  1. Limited Functionality: Lambda expressions are limited to implementing functional interfaces with a single abstract method, restricting their use in certain scenarios.
  2. Serialization: They are not serializable, which can be a limitation in certain distributed computing environments.
  3. Debugging: Debugging lambda expressions can be challenging, as they are anonymous and lack explicit names or stack traces.

Anonymous Inner Classes:

Advantages:

  1. Flexibility: Anonymous inner classes provide additional control and behavior compared to lambda expressions, allowing for more complex scenarios where fine-grained control is needed.
  2. Compatibility: They are available in earlier versions of Java, making them suitable for projects targeting older Java versions.
  3. Serialization: Anonymous inner classes are serializable, as they are named classes, which can be advantageous in distributed computing environments.
  4. Debugging: Debugging anonymous inner classes may be easier than lambda expressions, as they have explicit names and stack traces.

Disadvantages:

  1. Verbose Syntax: Anonymous inner classes have a more verbose syntax compared to lambda expressions, resulting in cluttered and less readable code.
  2. Performance Overhead: They may have slightly more overhead due to class instantiation, which can impact performance in performance-critical applications.
  3. Readability: The verbose syntax of anonymous inner classes can make code harder to read and maintain, especially in cases with nested classes.

In summary, Lambda Expressions offer a more concise and expressive syntax, making them suitable for functional-style programming, while Anonymous Inner Classes provide additional control and compatibility but with more verbosity and potential performance overhead. The choice between the two depends on the specific requirements of the project and the programming style preferences of the developer.

When to use lambda expressions vs. anonymous inner classes

Choosing between lambda expressions and anonymous inner classes depends on the specific requirements of the task at hand and the design preferences of the developer. Here are some guidelines for when to use each approach:

Use Lambda Expressions When:

  1. Functional Interfaces: Lambda expressions are ideal for implementing functional interfaces with a single abstract method, such as Runnable, Comparator, ActionListener, etc.
  2. Conciseness: Use lambda expressions when concise and readable code is preferred, as they eliminate boilerplate code and focus on the core logic.
  3. Functional Programming: If you’re adopting a functional programming style, lambda expressions are the preferred choice due to their support for higher-order functions and functional composition.
  4. Streams and Collections: Lambda expressions are well-suited for use with Java 8 Streams API and collections, where operations like filtering, mapping, and reducing can be performed in a concise and expressive manner.

Use Anonymous Inner Classes When:

  1. Additional Control: Use anonymous inner classes when you need additional control and behavior beyond what lambda expressions offer. This includes scenarios where multiple abstract methods need to be implemented or where access to instance variables or methods of the enclosing class is required.
  2. Compatibility: If you’re working on a project targeting older versions of Java that do not support lambda expressions (pre-Java 8), anonymous inner classes are the only option for achieving similar functionality.
  3. Explicit Naming: When you require explicit naming for debugging purposes or for better readability, anonymous inner classes provide named class instances that can be easily identified in stack traces and debugging tools.

In summary, lambda expressions are preferred for implementing functional interfaces and promoting functional programming paradigms, while anonymous inner classes offer additional control and compatibility, making them suitable for scenarios requiring finer-grained control or targeting older Java versions. Ultimately, the choice between lambda expressions and anonymous inner classes depends on the specific requirements of the task and the coding style preferences of the developer.

Best Practices and Tips

Best Practices and Tips for Lambda Expressions:

  1. Keep it Concise: Write lambda expressions that are concise and focused on the task at hand. Avoid unnecessary verbosity and keep the code clean and readable.
  2. Use Meaningful Variable Names: Choose meaningful variable names for lambda parameters to enhance readability and maintainability.
  3. Limit Complexity: Avoid complex logic within lambda expressions. If the logic becomes too complex, consider refactoring it into a separate method for clarity.
  4. Avoid Side Effects: Lambda expressions should ideally be stateless and not rely on external variables or have side effects. This promotes functional programming principles and makes code more predictable.
  5. Prefer Method References: Whenever possible, use method references instead of lambda expressions for improved readability. Method references offer a more declarative and concise way to refer to existing methods.
  6. Handle Exceptions Appropriately: Use try-catch blocks within lambda expressions to handle checked exceptions properly. Alternatively, consider wrapping checked exceptions in unchecked exceptions for cleaner code.
  7. Performance Considerations:
  • Use Primitive Types: Prefer primitive types over boxed types (e.g., int instead of Integer) in lambda expressions for better performance.
  • Minimize Object Creation: Avoid unnecessary object creation within lambda expressions, as it can impact performance. Consider reusing existing objects or using static methods where possible.
  1. Common Pitfalls to Avoid:
  • Accessing Mutable State: Be cautious when accessing mutable state within lambda expressions, as it can lead to unexpected behavior in multithreaded environments.
  • Capturing Variables: Avoid capturing variables from the enclosing scope unless they are effectively final. Capturing non-final variables can lead to subtle bugs and unexpected behavior.
  • Avoiding Complex Control Flow: Lambda expressions should ideally be kept simple and focused on a single task. Avoid complex control flow or nested lambda expressions, as they can reduce readability and maintainability.

In summary, follow these best practices and tips when writing lambda expressions to ensure clean, readable, and efficient code. By keeping lambda expressions concise, using meaningful variable names, handling exceptions appropriately, and avoiding common pitfalls, you can leverage the power of lambda expressions effectively in your Java codebase.

Lambda Expression Advanced Topics

Java 8 Lambda Functions Advanced Topics:

  1. Method References:
  • Method references provide a shorthand syntax for lambda expressions that call a single method. They can improve readability and reduce boilerplate code.
  • There are four types of method references: static method reference, instance method reference, constructor reference, and arbitrary object method reference.
  1. Constructor References:
  • Constructor references allow you to create instances of classes using lambda expressions. They provide a concise way to instantiate objects without explicitly writing a lambda expression for the constructor.
  • Constructor references are particularly useful when working with functional interfaces that represent factory methods or object instantiation.
  1. Effectively Combining Lambda Expressions and Streams:
  • Java 8 Streams API provides a powerful way to process collections of data using functional programming paradigms.
  • Lambda expressions can be seamlessly combined with Streams API to perform various operations such as filtering, mapping, reducing, and collecting elements.
  • When using lambda expressions with streams, it’s important to consider factors such as parallelism, performance, and exception handling for efficient and robust code.

Example of Method References:

// Static method reference
Function<String, Integer> parseInt = Integer::parseInt;

// Instance method reference
BiFunction<String, String, Boolean> startsWith = String::startsWith;

// Arbitrary object method reference
List<String> list = Arrays.asList("apple", "banana", "cherry");
list.forEach(System.out::println); // Method reference to System.out.println

// Constructor reference
Supplier<List<String>> listSupplier = ArrayList::new;

Example of Combining Lambda Expressions and Streams:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Filtering names starting with 'A' and converting to uppercase
List<String> filteredNames = names.stream()
                                 .filter(name -> name.startsWith("A"))
                                 .map(String::toUpperCase)
                                 .collect(Collectors.toList());

In summary, Java 8 Lambda Functions offer advanced features such as method references, constructor references, and seamless integration with the Streams API. By mastering these advanced topics, developers can write cleaner, more expressive, and efficient code leveraging the power of functional programming in Java.

Return type of lambda expression in java

In Java, the return type of a lambda expression is inferred based on the context in which the lambda expression is used.

If the lambda expression is used in a context where the return type can be determined unambiguously, such as when it is assigned to a functional interface with a single abstract method (SAM), the return type is inferred from the return type of that method.

For example, if you have a functional interface MyFunction with a method int apply(int x), and you assign a lambda expression to it like this:

MyFunction function = (x) -> x * x;

The return type of the lambda expression (x) -> x * x will be inferred to be int, because it matches the return type of the apply method in the MyFunction interface.

However, if the lambda expression is used in a context where the return type cannot be inferred unambiguously, such as when it is used in an assignment without specifying a target type, you may need to provide an explicit return type.

For example:

// Inferred return type: int
MyFunction function = (x) -> x * x;

// Explicit return type required for the lambda expression
Function<Integer, Integer> square = (Integer x) -> x * x;

In this case, the lambda expression (x) -> x * x is assigned to a Function<Integer, Integer>, which expects a return type of Integer, so an explicit return type is required for the lambda expression.

Conclusions

Conclusions on Java 8 Lambda Functions:

In conclusion, Java 8 Lambda Functions have revolutionized the way Java developers write code, bringing functional programming paradigms to the forefront of modern Java programming. Here’s a recap of key points and their importance:

  1. Recap of Key Points:
  • Lambda expressions provide a concise syntax for implementing functional interfaces, enabling a more expressive and modular codebase.
  • Method references and constructor references offer additional shorthand syntax for lambda expressions, enhancing readability and reducing boilerplate code.
  • Lambda expressions seamlessly integrate with the Streams API, allowing for efficient and parallel processing of collections of data.
  • Advanced topics such as method references, constructor references, and combining lambda expressions with streams further enhance the capabilities of Java 8 Lambda Functions.
  1. Importance of Lambda Expressions in Modern Java Programming:
  • Lambda expressions promote a more functional programming style, emphasizing immutability, composability, and modularity in code.
  • They enable developers to write cleaner, more expressive, and more maintainable code by reducing verbosity and boilerplate.
  • Lambda expressions facilitate the adoption of modern programming patterns and techniques, making Java codebases more flexible and adaptable to changing requirements.
  1. Future Trends and Advancements in Java Lambda Expressions:
  • The evolution of Java continues, with ongoing enhancements to lambda expressions and related features in newer Java versions.
  • Future advancements may include improved support for pattern matching, enhanced type inference, and more powerful functional programming constructs.
  • As Java evolves, lambda expressions are expected to play an increasingly central role in modern Java programming, driving innovation and enabling developers to write more efficient and scalable applications.

In summary, Java 8 Lambda Functions have transformed the Java programming landscape, empowering developers to write cleaner, more expressive, and more efficient code. As Java continues to evolve, lambda expressions will remain a cornerstone of modern Java programming, driving innovation and shaping the future of Java development.

FAQs

What are Lambda Expressions in Java 8?

Lambda expressions are a feature introduced in Java 8 that allow you to treat functionality as a method argument or create anonymous functions. They provide a concise syntax for implementing functional interfaces.

How do Lambda Expressions improve Java code?

Lambda expressions improve Java code by reducing verbosity and boilerplate, making it more concise and readable. They promote a functional programming style, enabling developers to write cleaner and more expressive code.

What are Method References in Java 8?

Method references provide a shorthand syntax for lambda expressions that call a single method. They enhance readability and reduce boilerplate code by allowing you to refer to existing methods directly.

What are Constructor References in Java 8?

Constructor references allow you to create instances of classes using lambda expressions. They provide a concise way to instantiate objects without explicitly writing a lambda expression for the constructor.

How do Lambda Expressions integrate with Streams API in Java 8?

Lambda expressions seamlessly integrate with the Streams API in Java 8, allowing for efficient and parallel processing of collections of data. They enable operations such as filtering, mapping, reducing, and collecting elements in a concise and expressive manner.

What is the return type of Lambda Expression in Java

In Java, the return type of a lambda expression is inferred based on the context in which the lambda expression is used.
If the lambda expression is used in a context where the return type can be determined unambiguously, such as when it is assigned to a functional interface with a single abstract method (SAM), the return type is inferred from the return type of that method.

Why we use lambda expression in java

Lambda expressions are used in Java for several reasons:
Conciseness: Lambda expressions provide a concise syntax for representing anonymous functions. They allow you to write code in a more compact and readable manner compared to traditional anonymous inner classes.
Expressiveness: Lambda expressions make code more expressive by focusing on the behavior rather than the mechanics of implementation. This leads to clearer and more understandable code.
Functional Programming: Lambda expressions enable functional programming paradigms in Java. They allow you to treat functions as first-class citizens, enabling higher-order functions, functional interfaces, and functional composition.
Stream API: Lambda expressions are a key part of the Java Stream API introduced in Java 8. They allow for functional-style operations on collections, such as filtering, mapping, and reducing elements, in a concise and expressive manner.
Concurrency: Lambda expressions simplify concurrent programming by providing a lightweight syntax for passing behavior to methods like Thread constructors or ExecutorService methods. They are particularly useful when working with Java’s concurrency utilities, such as CompletableFuture and parallelStream.
Callback Mechanism: Lambda expressions are commonly used as callback mechanisms, allowing you to define behavior that is executed asynchronously or in response to events.
Overall, lambda expressions enhance the readability, expressiveness, and flexibility of Java code, enabling developers to write cleaner, more modular, and more maintainable applications.

Advantages of lambda expression in java

Lambda expressions in Java offer several advantages:
Conciseness: Lambda expressions provide a more compact syntax for representing anonymous functions compared to traditional anonymous inner classes. This leads to cleaner and more readable code.
Expressiveness: Lambda expressions focus on the behavior rather than the mechanics of implementation, making code more expressive and understandable. They allow developers to express their intentions more clearly.
Functional Programming: Lambda expressions enable functional programming paradigms in Java. They facilitate the use of higher-order functions, functional interfaces, and functional composition, leading to more modular and composable code.
Stream API: Lambda expressions are a key part of the Java Stream API introduced in Java 8. They enable functional-style operations on collections, such as filtering, mapping, and reducing elements, in a concise and expressive manner.
Improved Readability: By eliminating boilerplate code and focusing on the core logic, lambda expressions enhance code readability. They make it easier to understand the purpose of a piece of code at a glance.
Concurrency: Lambda expressions simplify concurrent programming by providing a lightweight syntax for passing behavior to methods like Thread constructors or ExecutorService methods. They are particularly useful when working with Java’s concurrency utilities, such as CompletableFuture and parallelStream.
Reduced Overhead: Lambda expressions can reduce the amount of code you need to write, leading to fewer errors and less maintenance overhead. They allow you to express common patterns more succinctly.
Overall, lambda expressions enhance the readability, expressiveness, and flexibility of Java code, enabling developers to write cleaner, more modular, and more maintainable applications.


For Other Awesome Article visit AKCODING.COM and read articles in Medium

Share with