Welcome To Fusebes - Dev & Programming Blog

Layered Architecture
14
May
2021

What is a Layered Architecture?

One of the most common, and efficient, models deployed by enterprises is the Layered Architecture, also called the n-tiered pattern. It packs similar components together in a horizontal manner and is self-independent. What does that mean? 

It implies that the layers of the model are interconnected to each other but not interdependent. Similar components stay at the same level allowing the layers to be separated inadvertently based on the nature of the code. It is this isolation, that lends the software layers an independent nature. 

Consider an instance, wherein you’d want to switch from an Oracle database to an SQL. This shift may cause you to upend the database layer but will not have a domino effect on any other layer. 

Evidently, it serves a challenge for an enterprise software architect to create layers that separate from each other. Nevertheless, since the roles of each layer are clearly distinct, it accredits this software development architecture the following qualities:

  • It is easily maintainable as enterprise software developers with limited, or should we say pertinent, knowledge can be assigned to operate on a single layer.
  • You can test changes in layers separately from each other. 
  • Upgraded versions of the software can be implemented effortlessly. 

The flow of code is top-down, meaning it enters the presentation layer first and trickles down to the bottom-most layer that is the database layer. Each layer has a designated task based on the nature of the components it preserves. These could be checking the consistency of values within the code or reformatting the code altogether. 

Refactoring – a key  – way to lower frontend maintenance cost is a software development process by which developers change the internal shape and size of the code. They do it without affecting its external attributes can also be carried out in an n-tiered model.

Layered Architecture

This software development architecture can be customized to add layers to the presentation, business, persistence, and database levels. Such a model is called a Hybrid Layered architecture.

Benefits

  • Amongst the various types of software architecture, the layered variant suits enterprises that don’t want to go overboard with experimenting and want to stick to the traditional software architecture design patterns. 
  • Testing components become relatively easier as inter-dependencies are negligible in this format of software development engineering.
  • Considering many software frameworks were built with the backdrop of an n-tiered structure, applications built with them, as a result, happen to be in the layered format as well. 

Potential Drawbacks 

  • Larger applications tend to be resource-intensive if based on this format, therefore for such projects, it is advised to overlook the layered pattern. 
  • Although the layers are independent, yet the entire version of the software is installed as a single unit. Therefore, even if you update a single layer, you would have to re-install the entire apparatus all over again. 
  • Such systems are not scalable due to the coupling between the layers. 

Ideal For

The Layered architecture pattern suits the niche of LOB i.e. Line of Business Applications. These are applications that are essential to the functioning of the business itself. For instance, the accounts department of an organization needs software such as QuickBooks, Xero, Sage or Wave Accounting for keeping financial data. 

Similarly, the marketing team would demand a customer relationship management software slash tool to help them cope with the volume of interactions. In short, applications that do more than just CRUD (create, read, update, and delete) operations are suited to the layered architecture pattern. 

Kotlin Properties, Backing Fields, Getters and Setters
07
Mar
2021

Kotlin Properties, Backing Fields, Getters and Setters

You can declare properties inside Kotlin classes in the same way you declare any other variable. These properties can be mutable (declared using var) or immutable (declared using val).

Here is an example –

// User class with a Primary constructor that accepts three parameters
class User(_id: Int, _name: String, _age: Int) {
    // Properties of User class
    val id: Int = _id         // Immutable (Read only)
    var name: String = _name  // Mutable
    var age: Int = _age       // Mutable
}

You can get or set the properties of an object using the dot(.) notation like so –

val user = User(1, "Jack Sparrow", 44)

// Getting a Property
val name = user.name

// Setting a Property
user.age = 46

// You cannot set read-only properties
user.id = 2	// Error: Val cannot be assigned

Getters and Setters

Kotlin internally generates a default getter and setter for mutable properties, and a getter (only) for read-only properties.

It calls these getters and setters internally whenever you access or modify a property using the dot(.) notation.

Here is how the User class that we saw in the previous section looks like with the default getters and setters –

class User(_id: Int, _name: String, _age: Int) {
    val id: Int = _id
        get() = field
    
    var name: String = _name
        get() = field
        set(value) {
            field = value
        }
    
    var age: Int = _age
        get() = field
        set(value) {
            field = value
        }
}

You might have noticed two strange identifiers in all the getter and setter methods – field and value.

Let’s understand what these identifiers are for –

1. value

We use value as the name of the setter parameter. This is the default convention in Kotlin but you’re free to use any other name if you want.

The value parameter contains the value that a property is assigned to. For example, when you write user.name = "Bill Gates", the value parameter contains the assigned value “Bill Gates”.

2. Backing Field (field)

Backing field helps you refer to the property inside the getter and setter methods. This is required because if you use the property directly inside the getter or setter then you’ll run into a recursive call which will generate a StackOverflowError.

Let’s see that in action. Let’s modify the User class and refer to the properties directly inside the getters and setters instead of using the field identifier –

class User(_name: String, _age: Int) {
    var name: String = _name
        get() = name		  // Calls the getter recursively

    var age: Int = _age
        set(value) {
            age = value		  // Calls the setter recursively
        }
}

fun main(args: Array<String>) {
    val user = User("Jack Sparrow", 44)

    // Getting a Property
    println("${user.name}") // StackOverflowError

    // Setting a Property
    user.age = 45           // StackOverflowError

}

The above program will generate a StackOverflowError due to the recursive calls in the getter and setter methods. This is why Kotlin has the concept of backing fields. It makes storing the property value in memory possible. When you initialize a property with a value in the class, the initializer value is written to the backing field of that property –

class User(_id: Int) {
    val id: Int	= _id   // The initializer value is written to the backing field of the property
}

A backing field is generated for a property if

  • A custom getter/setter references it through the field identifier or,
  • It uses the default implementation of at least one of the accessors (getter or setter). (Remember, the default getter and setter references the field identifier themselves)

For example, a backing field is not generated for the id property in the following class because it neither uses the default implementation of the getter nor refers to the field identifier in the custom getter –

class User() {
    val id	// No backing field is generated
        get() = 0
}

Since a backing field is not generated, you won’t be able to initialize the above property like so –

class User(_id: Int) {
    val id: Int = _id	//Error: Initialization not possible because no backing field is generated which could store the initialized value in memory
        get() = 0
}

Creating Custom Getters and Setters

You can ditch Kotlin’s default getter/setter and define a custom getter and setter for the properties of your class.

Here is an example –

class User(_id: Int, _name: String, _age: Int) {
    val id: Int = _id

    var name: String = _name 
        // Custom Getter
        get() {     
            return field.toUpperCase()
        }     

    var age: Int = _age
        // Custom Setter
        set(value) {
            field = if(value > 0) value else throw IllegalArgumentException("Age must be greater than zero")
        }
}

fun main(args: Array<String>) {
    val user = User(1, "Jack Sparrow", 44)

    println("${user.name}") // JACK SPARROW

    user.age = -1           // Throws IllegalArgumentException: Age must be greater that zero
}

Conclusion

Thanks for reading folks. In this article, You learned how Kotlin’s getters and setters work and how to define a custom getter and setter for the properties of a class.

Build A Web Crawler with Java
02
Jan
2022

Build A Web Crawler with Java

Creating a web crawler is a smart way of retrieving useful information available online. With a web crawler, you can scan the Internet, browse through individual websites, and analyze and extract their content.

The Java programming language provides a simple way of building a web crawler and harvesting data from websites. You can use the extracted data for various use cases, such as for analytical purposes, providing a service that uses third-party data, or generating statistical data.

In this article, we’ll walk you through the process of building a web crawler using Java and ProxyCrawl.

What you’ll need

Typically, crawling web data involves creating a script that sends a request to the targeted web page, accesses its underlying HTML code, and scrapes the required information.

To accomplish that objective, you’ll need the following:

Before we develop the crawling logic, let’s clear the air why using ProxyCrawl is important for web crawling.

Why use ProxyCrawl for Crawling

ProxyCrawl is a powerful data crawling and scraping tool you can use to harvest information from websites fast and easily.

Here are some reasons why you should use it for crawling online data:

  • Easy to use It comes with a simple API that you can set up quickly without any programming hurdles. With just a few lines of code, you can start using the API to crawl websites and retrieve their content.
  • Supports advanced crawling ProxyCrawl allows you to perform advanced web crawling and scrape data from complicated websites. Since it supports JavaScript rendering, ProxyCrawl lets you extract data from dynamic websites. It offers a headless browser that allows you to extract what real users see on their web browsers—even if a site is created using modern frameworks like Angular or React.js.
  • Bypass crawling obstacles ProxyCrawl can handle all the restrictions often associated with crawling online data. It has an extensive network of proxies as well as more than 17 data centers around the world. You can use it to avoid access restrictions, resolve CAPTCHAs, and evade other anti-scraping measures implemented by web applications. What’s more, you can crawl websites while remaining anonymous; you’ll not worry about exposing your identity.
  • Free trial account You can test how ProxyCrawl works without giving out your payment details. The free account comes with 1,000 credits for trying out the tool’s capabilities.

How ProxyCrawl Works

ProxyCrawl provides the Crawling API for crawling and scraping data from websites. You can easily integrate the API in your Java development project and retrieve information from web pages smoothly.

Each request made to the Crawling API starts with the following base part:

https://api.proxycrawl.com

Also, you’ll need to add the following mandatory parameters to the API:

  • Authentication token
  • URL

The authentication token is a unique token that authorizes you to use the Crawling API. Once you sign up for an account, ProxyCrawl will give you two types of tokens:

  • Normal token This is for making generic crawling requests.
  • JavaScript token This is for crawling dynamic websites. It provides you with headless browser capabilities for crawling web pages rendered using JavaScript. As pointed out earlier, it’s a useful way of crawling advanced websites.

Here is how to add the authentication token to your API request:

https://api.proxycrawl.com/?token=INSERT_TOKEN

The second mandatory parameter is the URL to crawl. It should start with HTTP or HTTPS, and be completely encoded. Encoding converts the URL string into a format that can be transferred over the Internet validly and easily.

Here is how to insert the URL to your API request:

https://api.proxycrawl.com/?token=INSERT_TOKEN&url=INSERT_URL

If you run the above line—for example, on your terminal using cURL or pasting it on a browser’s address bar—it’ll execute the API request and return the entire HTML source code of the targeted web page.

It’s that easy and simple!

If you want to perform advanced crawling, you may add other parameters to the API request. For example, when using the JavaScript token, you can add the page_wait parameter to instruct the browser to wait for the specified number of milliseconds before the resulting HTML code is captured.

Here is an example:

https://api.proxycrawl.com/?token=INSERT_TOKEN&page_wait=1000&url=INSERT_URL

Building a Web Crawler in Java and ProxyCrawl

In this Java web crawling tutorial, we’ll use the HttpClient API to create the crawling logic. The API was introduced in Java 11, and it comes with lots of useful features for sending requests and retrieving their responses.

The HttpClient API supports both HTTP/1.1 and HTTP/2. By default, it uses the HTTP/2 protocol to send requests. If a request is sent to a server that does not already support HTTP/2, it will automatically be downgraded to HTTP/1.

Furthermore, its requests can be sent asynchronously or synchronously, it handles requests and response bodies as reactive-streams, and uses the common builder pattern.

The API is comprised of three core classes:

  • HttpRequest
  • HttpClient
  • HttpResponse

Let’s talk about each of them in more detail.

1. HttpRequest

The HttpRequest, as the name implies, is an object encapsulating the HTTP request to be sent. To create new instances of HttpRequest, call HttpRequest.newBuilder(). After it has been created, the request is immutable and can be sent multiple times.

The Builder class comes with different methods for configuring the request.

These are the most common methods:

  • URI method
  • Request method
  • Protocol version method
  • Timeout method

Let’s talk about each of them in more detail.

a) URI method

The first thing to do when configuring the request is to set the URL to crawl. We can do so by calling the uri() method on the Builder instance. We’ll also use the URI.create() method to create the URI by parsing the string of the URL we intend to crawl.

Here is the code:

String url =
URLEncoder.encode(“https://www.forextradingbig.com/7-reasons-why-you-should
-quit-forex-trading/”, StandardCharsets.UTF_8.name());

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(“https://api.proxycrawl.com/?token=INSERT_TOKEN&url=”
+ url))

Notice that we provided the URL string using ProxyCrawl’s settings. This is the web page we intend to scrape its contents.

We also encoded the URL using the Java URLEncoder class. As earlier mentioned, ProxyCrawl requires URLs to be encoded.

b) Request method

The next thing to do is to specify the HTTP method to be used for making the request. We can call any of the following methods from Builder:

  • GET()
  • POST()
  • PUT()
  • DELETE()

In this case, since we want to request data from the target web page, we’ll use the GET() method.

Here is the code:

HttpRequest request = HttpRequest.newBuilder()
.GET()

So far, HttpRequest has all the parameters that should be passed to HttpClient. However, you may need to include other parameters, such as the HTTP protocol version and timeout.

Let’s see how you can add the additional parameters.

c) Protocol version method

As earlier mentioned, the HttpClient API uses the HTTP/2 protocol by default. Nonetheless, you can specify the version of the HTTP protocol you want to use.

Here is the code:

HttpRequest request = HttpRequest.newBuilder()
.version(HttpClient.Version.HTTP_2)

d) Timeout method

You can set the amount of time to wait before a response is received. Once the defined period expires, an HttpTimeoutException will be thrown. By default, the timeout is set to infinity.

You can define timeout by calling the timeout() method on the builder instance. You’ll also need to pass the Duration object to specify the amount of time to wait.

Here is the code:

HttpRequest request = HttpRequest.newBuilder()
.timeout(Duration.ofSeconds(20))

2. HttpClient

The HttpClient class is the main entry point of the API—it acts as a container for the configuration details shared among multiple requests. It is the HTTP client used for sending requests and receiving responses.

You can call either the HttpClient.newBuilder() or the HttpClient.newHttpClient() method to instantiate it. After an instance of the HttpClient has been created, it’s immutable.

The HttpClient class offers several helpful and self-describing methods you can use when working with requests and responses.

These are some things you can do:

  • Set protocol version
  • Set redirect policy
  • Send synchronous and asynchronous requests

Let’s talk about each of them in more detail.

a) Set protocol version

As earlier mentioned, the HttpClient class uses the HTTP/2 protocol by default. However, you can set your preferred protocol version, either HTTP/1.1 or HTTP/2.

Here is an example:

HttpClient client = HttpClient.newBuilder()
.version(Version.HTTP_1_1)

b) Set redirect policy

If the targeted web page has moved to a different address, you’ll get a 3xx HTTP status code. Since the address of the new URI is usually provided with the status code information, setting the correct redirect policy can make HttpClient forward the request automatically to the new location.

You can set it by using the followRedirects() method on the Builder instance.

Here is an example:

HttpClient client = HttpClient.newBuilder()
.followRedirects(Redirect.NORMAL)

c) Send synchronous and asynchronous requests

HttpClient supports two ways of sending requests:

  • Synchronously by using the send() method. This blocks the client until the response is received, before continuing with the rest of the execution.

Here is an example:

HttpResponse<String> response = client.send(request,
BodyHandlers.ofString());

Note that we used BodyHandlers and called the ofString() method to return the HTML response as a string.

  • Asynchronously by using the sendAsync() method. This does not wait for the response to be received; it’s non-blocking. Once the sendAsync() method is called, it returns instantly with a CompletableFuture< HttpResponse >, which finalizes once the response is received. The returned CompletableFuture can be joined using various techniques to define dependencies among varied asynchronous tasks.

Here is an example:

CompletableFuture<HttpResponse<String>> response = HttpClient.newBuilder()
.sendAsync(request, HttpResponse.BodyHandler.ofString());

3. HttpResponse

The HttpResponse, as the name implies, represents the response received after sending an HttpRequest. HttpResponse offers different helpful methods for handling the received response.

These are the most important methods:

  • statusCode() This method returns the status code of the response. It’s of int type
  • Body() This method returns a body for the response. The return type is based on the kind of response BodyHandler parameter that is passed to the send() method.

Here is an example:

// Handling the response body as a String
HttpResponse<String> response = client
.send(request, BodyHandlers.ofString());

// Printing response body
System.out.println(response.body());

// Printing status code
System.out.println(response.statusCode());
// Handling the response body as a file
HttpResponse<Path> response = client
.send(request, BodyHandlers.ofFile(Paths.get(“myexample.html”)));

Synchronous Example

Here is an example that uses the HttpClient synchronous method to crawl a web page and output its content:

package javaHttpClient;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.StandardCharsets;

public class SyncExample {

public static void main(String[] args) throws IOException, InterruptedException {

// Encoding the URL
String url = URLEncoder.encode(“https://www.forextradingbig.com/7-reasons-why-you-should-quit-forex-trading/”, StandardCharsets.UTF_8.name());

// Instantiating HttpClient
HttpClient client = HttpClient.newHttpClient();

// Configuring HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.GET()
.uri(URI.create(“https://api.proxycrawl.com/?token=INSERT_TOKEN&url=” + url))
.build();

// Handling the response
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println(response.body());
}

}

Here is the output:

Asynchronous Example

When using the HttpClient asynchronous method to crawl a web page, the sendAsync() method is called, instead of send().

Here is an example:

package javaHttpClient;

import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class AsyncExample {


public static void main(String[] args) throws IOException, InterruptedException, ExecutionException, TimeoutException {

// Encoding the URL
String url = URLEncoder.encode(“https://www.forextradingbig.com/7-reasons-why-you-should-quit-forex-trading/”, StandardCharsets.UTF_8.name());

// Instantiating HttpClient
HttpClient client = HttpClient.newHttpClient();

// Configuring HttpRequest
HttpRequest request = HttpRequest.newBuilder()
.GET()
.version(HttpClient.Version.HTTP_2)
.uri(URI.create(“https://api.proxycrawl.com/?token=INSERT_TOKEN&url=” + url))
.build();

// Handling the response
CompletableFuture<HttpResponse<String>> response =
client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

String result = response.thenApply(HttpResponse::body).get(5, TimeUnit.SECONDS);

System.out.println(result);

}

}

Conclusion

That’s how to build a web crawler in Java. The HttpClient API, which was introduced in Java 11, makes it easy to send and handle responses from a server.

And if the API is combined with a versatile tool like ProxyCrawl, it can make web crawling tasks smooth and rewarding.

With ProxyCrawl, you can create a scraper that can help you to retrieve information from websites anonymously and without worrying about being blocked.

It’s the tool you need to take your crawling efforts to the next level.

Click here to create a free ProxyCrawl account.

Happy scraping!

Two Number Sum Problem Solution
06
Feb
2021

Two Number Sum Problem Solution

Two Number Sum Problem Statement

Given an array of integers, return the indices of the two numbers whose sum is equal to a given target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

Example:

Given nums = [2, 7, 11, 15], target = 9.

The output should be [0, 1]. 
Because nums[0] + nums[1] = 2 + 7 = 9.

Two Number Sum Problem solution in Java

METHOD 1. Naive approach: Use two for loops

The naive approach is to just use two nested for loops and check if the sum of any two elements in the array is equal to the given target.

Time complexity: O(n^2)

import java.util.HashMap;
import java.util.Scanner;
import java.util.Map;

class TwoSum {

    // Time complexity: O(n^2)
    private static int[] findTwoSum_BruteForce(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[i] + nums[j] == target) {
                    return new int[] { i, j };
                }
            }
        }
        return new int[] {};
    }


    public static void main(String[] args) {
        Scanner keyboard = new Scanner(System.in);

        int n = keyboard.nextInt();
        int[] nums = new int[n];

        for(int i = 0; i < n; i++) {
            nums[i] = keyboard.nextInt();
        }
        int target = keyboard.nextInt();

        keyboard.close();

        int[] indices = findTwoSum_BruteForce(nums, target);

        if (indices.length == 2) {
            System.out.println(indices[0] + " " + indices[1]);
        } else {
            System.out.println("No solution found!");
        }
    }
}
# Output
$ javac TwoSum.java
$ java TwoSum
4 2 7 11 15
9
0 1

METHOD 2. Use a HashMap (Most efficient)

You can use a HashMap to solve the problem in O(n) time complexity. Here are the steps:

  1. Initialize an empty HashMap.
  2. Iterate over the elements of the array.
  3. For every element in the array –
    • If the element exists in the Map, then check if it’s complement (target - element) also exists in the Map or not. If the complement exists then return the indices of the current element and the complement.
    • Otherwise, put the element in the Map, and move to the next iteration.

Time complexity: O(n)

import java.util.HashMap;
import java.util.Scanner;
import java.util.Map;

class TwoSum {
    
    // Time complexity: O(n)
    private static int[] findTwoSum(int[] nums, int target) {
        Map<Integer, Integer> numMap = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (numMap.containsKey(complement)) {
                return new int[] { numMap.get(complement), i };
            } else {
                numMap.put(nums[i], i);
            }
        }
        return new int[] {};
    }
}

METHOD 3. Use Sorting along with the two-pointer sliding window approach

There is another approach which works when you need to return the numbers instead of their indexes. Here is how it works:

  1. Sort the array.
  2. Initialize two variables, one pointing to the beginning of the array (left) and another pointing to the end of the array (right).
  3. Loop until left < right, and for each iteration
    • if arr[left] + arr[right] == target, then return the indices.
    • if arr[left] + arr[right] < target, increment the left index.
    • else, decrement the right index.

This approach is called the two-pointer sliding window approach. It is a very common pattern for solving array related problems.

Time complexity: O(n*log(n))

import java.util.Scanner;
import java.util.Arrays;

class TwoSum {

    // Time complexity: O(n*log(n))
    private static int[] findTwoSum_Sorting(int[] nums, int target) {
        Arrays.sort(nums);
        int left = 0;
        int right = nums.length - 1;
        while(left < right) {
            if(nums[left] + nums[right] == target) {
                return new int[] {nums[left], nums[right]};
            } else if (nums[left] + nums[right] < target) {
                left++;
            } else {
                right--;
            }
        }
        return new int[] {};
    }
}

Liked the Article? Share it on Social media!

How to get current epoch timestamp in Java
03
Mar
2021

How to get current epoch timestamp in Java

In this article, you’ll learn how to get the current epoch timestamp in milliseconds precision in Java.

Get current timestamp in Java using System.currentTimeMillis()

public class CurrentEpochTimestampExample {
    public static void main(String[] args) {
        // Get epoch timestamp using System.currentTimeMillis()
        long currentTimestamp = System.currentTimeMillis();
        System.out.println("Current epoch timestamp in millis: " + currentTimestamp);
    }
}

Get current timestamp in Java using the Instant class

import java.time.Instant;

public class CurrentEpochTimestampExample {
    public static void main(String[] args) {
        // Get current timestamp using Instant
        currentTimestamp = Instant.now().toEpochMilli();
        System.out.println("Current epoch timestamp in millis: " + currentTimestamp);

    }
}
Reading and Writing Environment Variables in Go
08
Mar
2021

Reading and Writing Environment Variables in Go

An environment variable is a way to supply dynamic configuration information to programs at runtime. Environment variables are often used to make the same program work in different environments like Local, QA, or Production.

Get, Set, Unset, and Expand environment variables in Go

The following program demonstrates how to work with environment variables in Go. It makes use of the following functions provided by the os package:

  • os.Setenv(key, value): Set an environment variable.
  • os.Getenv(key): Get the value of an environment variable. If the environment variable is not present, it returns empty. To distinguish between an empty value and an unset value, use LookupEnv
  • os.Unsetenv(key): Unset an environment variable.
  • os.LookupEnv(key): Get the value of an environment variable and a boolean indicating whether the environment variable is present or not. It returns a string and a boolean – The boolean will be false if the environment variable is not present.
  • os.ExpandEnv(str): Expand a string by replacing ${var} or $var in the string according to the values of the current environment variables.
package main

import (
	"fmt"
	"os"
)

func main() {
	// Set an Environment Variable
	os.Setenv("DB_HOST", "localhost")
	os.Setenv("DB_PORT", "5432")
	os.Setenv("DB_USERNAME", "root")
	os.Setenv("DB_PASSWORD", "admin")
	os.Setenv("DB_NAME", "test")

	// Get the value of an Environment Variable
	host := os.Getenv("DB_HOST")
	port := os.Getenv("DB_PORT")
	fmt.Printf("Host: %s, Port: %s\n", host, port)

	// Unset an Environment Variable
	os.Unsetenv("DB_PORT")
	fmt.Printf("After unset, Port: %s\n", os.Getenv("DB_PORT"))

	/*
		Get the value of an environment variable and a boolean indicating whether the
		environment variable is present or not.
	*/
	driver, ok := os.LookupEnv("DB_DRIVER")
	if !ok {
		fmt.Println("DB_DRIVER is not present")
	} else {
		fmt.Printf("Driver: %s\n", driver)
	}

	// Expand a string containing environment variables in the form of $var or ${var}
	dbURL := os.ExpandEnv("postgres://$DB_USERNAME:$DB_PASSWORD@DB_HOST:$DB_PORT/$DB_NAME")
	fmt.Println("DB URL: ", dbURL)

}
# Output
Host: localhost, Port: 5432
After unset, Port:
DB_DRIVER is not present
DB URL:  postgres://root:admin@DB_HOST:/test

List and Clear all environment variables in Go

  • os.Environ(): This function returns a []string containing all the environment variables in the form of key=value.
  • os.Clearenv(): This function deletes all the environment variables. It may come in handy while writing tests to start with a clean environment.

The following example demonstrates how to use these two functions:

package main

import (
	"fmt"
	"os"
	"strings"
)

func main() {

	// Environ returns a copy of strings representing the environment,
	// in the form "key=value".
	for _, env := range os.Environ() {
		// env is
		envPair := strings.SplitN(env, "=", 2)
		key := envPair[0]
		value := envPair[1]

		fmt.Printf("%s : %s\n", key, value)
	}

	// Delete all environment variables
	os.Clearenv()

	fmt.Println("Number of environment variables: ", len(os.Environ()))
}
# Output
TERM_SESSION_ID : w0t0p1:70C49068-9C87-4032-9C9B-49FB6B86687B
PATH : /Users/fusebes/.nvm/versions/node/v10.0.0/bin:/usr/local/sbin:/usr/local/sbin:/Users/fusebes/protobuf/bin:/Users/fusebes/go/bin:/Users/fusebes/vaultproject:/Users/fusebes/google-cloud-sdk/bin:/Users/fusebes/.rbenv/bin:/Users/fusebes/.rbenv/shims:/Users/fusebes/anaconda3/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/fusebes/Library/Android/sdk/platform-tools:/opt/flutter/bin
.... # Output truncated for brevity

Number of environment variables:  0
Kotlin Functions, Default and Named Arguments, Varargs and Function Scopes
07
Mar
2021

Kotlin Functions, Default and Named Arguments, Varargs and Function Scopes

Functions are the basic building block of any program. In this article, you’ll learn how to declare and call functions in Kotlin. You’ll also learn about Function scopes, Default arguments, Named Arguments, and Varargs.

Defining and Calling Functions

You can declare a function in Kotlin using the fun keyword. Following is a simple function that calculates the average of two numbers –

fun avg(a: Double, b: Double): Double {
    return  (a + b)/2
}

Calling a function is simple. You just need to pass the required number of parameters in the function name like this –

avg(4.6, 9.0)  // 6.8

Following is the general syntax of declaring a function in Kotlin.

fun functionName(param1: Type1, param2: Type2,..., paramN: TypeN): Type {
	// Method Body
}

Every function declaration has a function name, a list of comma-separated parameters, an optional return type, and a method body. The function parameters must be explicitly typed.

Single Expression Functions

You can omit the return type and the curly braces if the function returns a single expression. The return type is inferred by the compiler from the expression –

fun avg(a: Double, b: Double) = (a + b)/2
avg(10.0, 20.0)  // 15.0

Note that, Unlike other statically typed languages like Scala, Kotlin does not infer return types for functions with block bodies. Therefore, Functions with block body must always specify return types explicitly.

Unit returning Functions

Functions which don’t return anything has a return type of Unit. The Unit type corresponds to void in Java.

fun printAverage(a: Double, b: Double): Unit {
    println("Avg of ($a, $b) = ${(a + b)/2}")
}

printAverage(10.0, 30.0)  // Avg of (10.0, 30.0) = 20.0

Note that, the Unit type declaration is completely optional. So you can also write the above function declaration like this –

fun printAverage(a: Double, b: Double) {
    println("Avg of ($a, $b) = ${(a + b)/2}")
}

Function Default Arguments

Kotlin supports default arguments in function declarations. You can specify a default value for a function parameter. The default value is used when the corresponding argument is omitted from the function call.

Consider the following function for example –

fun displayGreeting(message: String, name: String = "Guest") {
    println("Hello $name, $message")
}

If you call the above function with two arguments, it works just like any other function and uses the values passed in the arguments –

displayGreeting("Welcome to the CalliCoder Blog", "John") // Hello John, Welcome to the CalliCoder Blog

However, If you omit the argument that has a default value from the function call, then the default value is used in the function body –

displayGreeting("Welcome to the CalliCoder Blog") // Hello Guest, Welcome to the CalliCoder Blog

If the function declaration has a default parameter preceding a non-default parameter, then the default value cannot be used while calling the function with position-based arguments.

Consider the following function –

fun arithmeticSeriesSum(a: Int = 1, n: Int, d: Int = 1): Int {
    return n/2 * (2*a + (n-1)*d)
}

While calling the above function, you can not omit the argument a from the function call and selectively pass a value for the non-default parameter n –

arithmeticSeriesSum(10) // error: no value passed for parameter n

When you call a function with position-based arguments, the first argument corresponds to the first parameter, the second argument corresponds to the second parameter, and so on.

So for passing a value for the 2nd parameter, you need to specify a value for the first parameter as well –

arithmeticSeriesSum(1, 10)  // Result = 55

However, The above use-case of selectively passing a value for a parameter is solved by another feature of Kotlin called Named Arguments.

Function Named Arguments

Kotlin allows you to specify the names of arguments that you’re passing to the function. This makes the function calls more readable. It also allows you to pass the value of a parameter selectively if other parameters have default values.

Consider the following arithmeticSeriesSum() function that we defined in the previous section –

fun arithmeticSeriesSum(a: Int = 1, n: Int, d: Int = 1): Int {
    return n/2 * (2*a + (n-1)*d)
}

You can specify the names of arguments while calling the function like this –

arithmeticSeriesSum(n=10)  // Result = 55

The above function call will use the default values for parameters a and d.

Similarly, you can call the function with all the parameters like this –

arithmeticSeriesSum(a=3, n=10, d=2)  // Result = 120

You can also reorder the arguments if you’re specifying the names –

arithmeticSeriesSum(n=10, d=2, a=3)  // Result = 120

You can use a mix of named arguments and position-based arguments as long as all the position-based arguments are placed before the named arguments –

arithmeticSeriesSum(3, n=10)  // Result = 75

The following function call is not allowed since it contains position-based arguments after named arguments –

arithmeticSeriesSum(n=10, 2) // error: mixing named and positioned arguments is not allowed

Variable Number of Arguments (Varargs)

You can pass a variable number of arguments to a function by declaring the function with a vararg parameter.

Consider the following sumOfNumbers() function which accepts a vararg of numbers –

fun sumOfNumbers(vararg numbers: Double): Double {
    var sum: Double = 0.0
    for(number in numbers) {
        sum += number
    }
    return sum
}

You can call the above function with any number of arguments –

sumOfNumbers(1.5, 2.0)  // Result = 3.5

sumOfNumbers(1.5, 2.0, 3.5, 4.0, 5.8, 6.2)  // Result = 23.0

sumOfNumbers(1.5, 2.0, 3.5, 4.0, 5.8, 6.2, 8.1, 12.4, 16.5)  // Result = 60.0

In Kotlin, a vararg parameter of type T is internally represented as an array of type T (Array<T>) inside the function body.

A function may have only one vararg parameter. If there are other parameters following the vararg parameter, then the values for those parameters can be passed using the named argument syntax –

fun sumOfNumbers(vararg numbers: Double, initialSum: Double): Double {
    var sum = initialSum
    for(number in numbers) {
        sum += number
    }
    return sum
}
sumOfNumbers(1.5, 2.5, initialSum=100.0) // Result = 104.0

Spread Operator

Usually, we pass the arguments to a vararg function one-by-one. But if you already have an array and want to pass the elements of the array to the vararg function, then you can use the spread operator like this –

val a = doubleArrayOf(1.5, 2.6, 5.4)
sumOfNumbers(*a)  // Result = 9.5

Function Scope

Kotlin supports functional programming. Functions are first-class citizens in the language.

Unlike Java where every function needs to be encapsulated inside a class, Kotlin functions can be defined at the top level in a source file.

In addition to top-level functions, you also have the ability to define member functions, local functions, and extension functions.

1. Top Level Functions

Top level functions in Kotlin are defined in a source file outside of any class. They are also called package level functions because they are a member of the package in which they are defined.

The main() method itself is a top-level function in Kotlin since it is defined outside of any class.

Let’s now see an example of a top-level function. Check out the following findNthFibonacciNo() function which is defined inside a package named maths –

package maths

fun findNthFibonacciNo(n: Int): Int {
    var a = 0
    var b = 1
    var c: Int

    if(n == 0) {
        return a
    }

    for(i in 2..n) {
        c = a+b
        a = b
        b = c
    }
    return b
}

You can access the above function directly inside the maths package –

package maths

fun main(args: Array<String>) {
    println("10th fibonacci number is - ${findNthFibonacciNo(10)}")
}
//Outputs - 10th fibonacci number is - 55

However, If you want to call the findNthFibonacciNo() function from other packages, then you need to import it as in the following example –

package test
import maths.findNthFibonacciNo

fun main(args: Array<String>) {
    println("10th fibonacci number is - ${findNthFibonacciNo(10)}")
}

2. Member Functions

Member functions are functions which are defined inside a class or an object.

class User(val firstName: String, val lastName: String) {

	// Member function
    fun getFullName(): String {
        return firstName + " " + lastName
    }
}

Member functions are called on the objects of the class using the dot(.) notation –

val user = User("Bill", "Gates") // Create an object of the class
println("Display Name : ${user.getFullName()}") // Call the member function

3. Local/Nested Functions

Kotlin allows you to nest function definitions. These nested functions are called Local functions. Local functions bring more encapsulation and readability to your program –

fun findBodyMassIndex(weightInKg: Double, heightInCm: Double): Double {
    // Validate the arguments
    if(weightInKg <= 0) {
        throw IllegalArgumentException("Weight must be greater than zero")
    }
    if(heightInCm <= 0) {
        throw IllegalArgumentException("Height must be greater than zero")
    }

    fun calculateBMI(weightInKg: Double, heightInCm: Double): Double {
        val heightInMeter = heightInCm / 100
        return weightInKg / (heightInMeter * heightInMeter)
    }

    // Calculate BMI using the nested function
    return calculateBMI(weightInKg, heightInCm)
}

Local functions can access local variables of the outer function. So the above function is equivalent to the following –

fun findBodyMassIndex(weightInKg: Double, heightInCm: Double): Double {
    if(weightInKg <= 0) {
        throw IllegalArgumentException("Weight must be greater than zero")
    }
    if(heightInCm <= 0) {
        throw IllegalArgumentException("Height must be greater than zero")
    }

	// Nested function has access to the local variables of the outer function
    fun calculateBMI(): Double {
        val heightInMeter = heightInCm / 100
        return weightInKg / (heightInMeter * heightInMeter)
    }

    return calculateBMI()
}

Conclusion

Congratulations folks! In this article, you learned how to define and call functions in Kotlin, how to use default and named arguments, how to define and call functions with a variable number of arguments, and how to define top-level functions, member functions and local/nested functions

In future articles, I’ll write about extension functions, higher order functions and lambdas. So Stay tuned!

As always, Thank you for reading. Happy Kotlin Koding 🙂

Creating PDF with Java and iText
05
Mar
2021

Creating PDF with Java and iText

Java and PDF with iText. This article demonstrate how to create PDF files with Java and the iText library. In this tutorial iText version 5.0.x is used

iText is a Java library originally created by Bruno Lowagie which allows to create PDF, read PDF and manipulate them. The following tutorial will show how to create PDF files with iText. This tutorial assumes that you have basis in Java and Eclipse knowledge.

iText has a hierarchical structure. The smallest text unit is a “Chunk” which is a String with a pre-defined font. A “Phrase” combines several Chunks and allows to define line spacing. “Paragraph” is a subclass of “Phrase” and allows to define more layout attributes, e.g. margins. The class “Anchor” is a subclass of “Paragraph” and serves as the basis for hyperlinks in the generated PDF.

Create the following class “FirstPdf.java” . I assume that the code is pretty much self-explaining. I tried to add lots of comments to make it easier to understand. For more complex examples have a look at the iText Homepage.

package de.fusebes.itext.write;

import java.io.FileOutputStream;
import java.util.Date;

import com.itextpdf.text.Anchor;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chapter;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.List;
import com.itextpdf.text.ListItem;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Section;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;


public class FirstPdf {
    private static String FILE = "c:/temp/FirstPdf.pdf";
    private static Font catFont = new Font(Font.FontFamily.TIMES_ROMAN, 18,
            Font.BOLD);
    private static Font redFont = new Font(Font.FontFamily.TIMES_ROMAN, 12,
            Font.NORMAL, BaseColor.RED);
    private static Font subFont = new Font(Font.FontFamily.TIMES_ROMAN, 16,
            Font.BOLD);
    private static Font smallBold = new Font(Font.FontFamily.TIMES_ROMAN, 12,
            Font.BOLD);

    public static void main(String[] args) {
        try {
            Document document = new Document();
            PdfWriter.getInstance(document, new FileOutputStream(FILE));
            document.open();
            addMetaData(document);
            addTitlePage(document);
            addContent(document);
            document.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // iText allows to add metadata to the PDF which can be viewed in your Adobe
    // Reader
    // under File -> Properties
    private static void addMetaData(Document document) {
        document.addTitle("My first PDF");
        document.addSubject("Using iText");
        document.addKeywords("Java, PDF, iText");
        document.addAuthor("Lars Vogel");
        document.addCreator("Lars Vogel");
    }

    private static void addTitlePage(Document document)
            throws DocumentException {
        Paragraph preface = new Paragraph();
        // We add one empty line
        addEmptyLine(preface, 1);
        // Lets write a big header
        preface.add(new Paragraph("Title of the document", catFont));

        addEmptyLine(preface, 1);
        // Will create: Report generated by: _name, _date
        preface.add(new Paragraph(
                "Report generated by: " + System.getProperty("user.name") + ", " + new Date(), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                smallBold));
        addEmptyLine(preface, 3);
        preface.add(new Paragraph(
                "This document describes something which is very important ",
                smallBold));

        addEmptyLine(preface, 8);

        preface.add(new Paragraph(
                "This document is a preliminary version and not subject to your license agreement or any other agreement with vogella.com ;-).",
                redFont));

        document.add(preface);
        // Start a new page
        document.newPage();
    }

    private static void addContent(Document document) throws DocumentException {
        Anchor anchor = new Anchor("First Chapter", catFont);
        anchor.setName("First Chapter");

        // Second parameter is the number of the chapter
        Chapter catPart = new Chapter(new Paragraph(anchor), 1);

        Paragraph subPara = new Paragraph("Subcategory 1", subFont);
        Section subCatPart = catPart.addSection(subPara);
        subCatPart.add(new Paragraph("Hello"));

        subPara = new Paragraph("Subcategory 2", subFont);
        subCatPart = catPart.addSection(subPara);
        subCatPart.add(new Paragraph("Paragraph 1"));
        subCatPart.add(new Paragraph("Paragraph 2"));
        subCatPart.add(new Paragraph("Paragraph 3"));

        // add a list
        createList(subCatPart);
        Paragraph paragraph = new Paragraph();
        addEmptyLine(paragraph, 5);
        subCatPart.add(paragraph);

        // add a table
        createTable(subCatPart);

        // now add all this to the document
        document.add(catPart);

        // Next section
        anchor = new Anchor("Second Chapter", catFont);
        anchor.setName("Second Chapter");

        // Second parameter is the number of the chapter
        catPart = new Chapter(new Paragraph(anchor), 1);

        subPara = new Paragraph("Subcategory", subFont);
        subCatPart = catPart.addSection(subPara);
        subCatPart.add(new Paragraph("This is a very important message"));

        // now add all this to the document
        document.add(catPart);

    }

    private static void createTable(Section subCatPart)
            throws BadElementException {
        PdfPTable table = new PdfPTable(3);

        // t.setBorderColor(BaseColor.GRAY);
        // t.setPadding(4);
        // t.setSpacing(4);
        // t.setBorderWidth(1);

        PdfPCell c1 = new PdfPCell(new Phrase("Table Header 1"));
        c1.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(c1);

        c1 = new PdfPCell(new Phrase("Table Header 2"));
        c1.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(c1);

        c1 = new PdfPCell(new Phrase("Table Header 3"));
        c1.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(c1);
        table.setHeaderRows(1);

        table.addCell("1.0");
        table.addCell("1.1");
        table.addCell("1.2");
        table.addCell("2.1");
        table.addCell("2.2");
        table.addCell("2.3");

        subCatPart.add(table);

    }

    private static void createList(Section subCatPart) {
        List list = new List(true, false, 10);
        list.add(new ListItem("First point"));
        list.add(new ListItem("Second point"));
        list.add(new ListItem("Third point"));
        subCatPart.add(list);
    }

    private static void addEmptyLine(Paragraph paragraph, int number) {
        for (int i = 0; i < number; i++) {
            paragraph.add(new Paragraph(" "));
        }
    }
}

The resulting pdf should look like the following.

pdfwrite10
Microservices Architecture
14
May
2021

Microservices Architecture

Microservices are defined as a self-regulating, and independent codebase that can be written and maintained even by a small team of developers. Microservices Architecture consists of such loosely coupled services with each service responsible for the execution of its associated business logic. 

The services are separated from each other based on the nature of their domains and belong to a mini-microservice pool. Enterprise mobile app developers leverage the capabilities of this architecture especially for complex applications. 

Microservices Architecture allows developers to release versions of software thanks to sophisticated automation of software building, testing, and deployment – something that acts as a prime differentiation point between Microservices and Monolithic architecture.

Microservices Architecture

Benefits

  • Since the services are bifurcated into pools, the architecture design pattern makes the system highly fault-tolerant. In other words, the whole software won’t collapse on its head even if some microservices cease to function. 
  • An enterprise mobile app development company working on such an architecture for clients can deploy multiple programming languages to build different microservices for their specific purpose. Therefore the technology stack can be kept updated with the latest upgrades in computing. 
  • This architecture is a perfect fit for applications that need to scale. Since the services are already independent of each other, they can scale individually rather than overloading the entire system with the need to expand. 
  • Services can be integrated into any application depending upon the scope of work. 

Potential Drawbacks 

  • Since each service is unique in its ability to contribute to the whole codebase, it could be challenging for an enterprise mobile application development company to interlink all and operate so many distinctive services seamlessly. 
  • Developers must define a standard protocol for all services to adhere to. It is important to do so, as the decentralized approach towards coding microservices in multiple languages can pose serious issues while debugging. 
  • Each microservice with its limited environment is responsible to maintain the integrity of the data. It is up to the architects of such a system to come up with a universally consistent data integrity protocol, wherever possible. 
  • You definitely need the best of breed professionals to design such a system for you as the technology stack keeps changing. 

Ideal For

Use Microservices Architecture for apps in which a specific segment will be used heavily than the others and would need a sporadic burst of scaling. Instead of a standalone application you may also deploy this for a service that provides functionality to other applications of the system. 

Kotlin Operators with Examples
07
Mar
2021

Kotlin Operators with Examples

In the previous article, you learned how to create variables and what are various basic data types available in Kotlin for creating variables.

In this article, you’ll learn what are various operators provided by kotlin to perform operations on basic data types.

Operations on Numeric Types

Just like other languages, Kotlin provides various operators to perform computations on numbers –

  • Arithmetic operators (+-*/%)
  • Comparison operators (==!=<><=>=)
  • Assignment operators (+=-=*=/=%=)
  • Increment & Decrement operators (++--)

Following are few examples that demonstrate the usage of above operators –

var a = 10
var b = 20
var c = ((a + b) * ( a + b))/2   // 450

var isALessThanB = a < b   // true

a++     // a now becomes 11
b += 5  // b equals to 25 now

Understanding how operators work in Kotlin

Everything in Kotlin is an object, even the basic data types like IntCharDoubleBoolean etc. Kotlin doesn’t have separate primitive types and their corresponding boxed types like Java.

Note that Kotlin may represent basic types like IntCharBoolean etc. as primitive values at runtime to improve performance, but for the end users, all of them are objects.

Since all the data types are objects, the operations on these types are internally represented as function calls.

For example, the addition operation a + b between two numbers a and b is represented as a function call a.plus(b) –

var a = 4
var b = 5

println(a + b)

// equivalent to
println(a.plus(b))

All the operators that we looked at in the previous section have a symbolic name which is used to translate any expression containing those operators into the corresponding function calls –

ExpressionTranslates to
a + ba.plus(b)
a – ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.rem(b)
a++a.inc()
a−−a.dec()
a > ba.compareTo(b) > 0
a < ba.compareTo(b) < 0
a += ba.plusAssign(b)

You can check out other expressions and their corresponding function calls on Kotlin’s reference page.

The concept of translating such expressions to function calls enable operator overloading in Kotlin. For example, you can provide implementation for the plus function in a class defined by you, and then you’ll be able to add the objects of that class using + operator like this – object1 + object2.

Kotlin will automatically convert the addition operation object1 + object2 into the corresponding function call object1.plus(object2) (Think of a ComplexNumber class with the + operator overloaded).

You’ll learn more about operator overloading in a future article.

Note that the operations on basic types like IntCharDoubleBoolean etc. are optimized and do not include the overhead of function calls.

Bitwise Operators

Unlike C, C++ and Java, Kotlin doesn’t have bitwise operators like |(bitwise-or), &(bitwise-and), ^(bitwise-xor), << (signed left shift), >>(signed right shift) etc.

For performing bitwise operations, Kotlin provides following methods that work for Int and Long types –

  • shl – signed shift left (equivalent of << operator)
  • shr – signed shift right (equivalent of >> operator)
  • ushr– unsigned shift right (equivalent of >>> operator)
  • and – bitwise and (equivalent of & operator)
  • or – bitwise or (equivalent of | operator)
  • xor – bitwise xor (equivalent of ^ operator)
  • inv – bitwise complement (equivalent of ~ operator)

Here are few examples demonstrating how to use above functions –

1 shl 2   // Equivalent to 1.shl(2), Result = 4
16 shr 2  // Result = 4
2 and 4   // Result = 0
2 or 3    // Result = 3
4 xor 5   // Result = 1
4.inv()   // Result = -5

All the bitwise functions, except inv(), can be called using infix notation. The infix notation of 2.and(4) is 2 and 4. Infix notation allows you to write function calls in a more intuitive way.

Operations on Boolean Types

Kotlin supports following logical operators for performing operations on boolean types –

  • || – Logical OR
  • && – Logical AND
  • !   – Logical NOT

Here are few examples of logical operators –

2 == 2 && 4 != 5  // true
4 > 5 && 2 < 7    // false
!(7 > 12 || 14 < 18)  // false

Logical operators are generally used in control flow statements like ifif-elsewhile etc., to test the validity of a condition.

Operations on Strings

String Concatenation

The + operator is overloaded for String types. It performs String concatenation –

var firstName = "Rajeev"
var lastName = "Singh"
var fullName = firstName + " " + lastName	// "Rajeev Singh"

String Interpolation

Kotlin has an amazing feature called String Interpolation. This feature allows you to directly insert a template expression inside a String. Template expressions are tiny pieces of code that are evaluated and their results are concatenated with the original String.

A template expression is prefixed with $ symbol. Following is an example of String interpolation –

var a = 12
var b = 18
println("Avg of $a and $b is equal to ${ (a + b)/2 }") 

// Prints - Avg of 12 and 18 is equal to 15

If the template expression is a simple variable, you can write it like $variableName. If it is an expression then you need to insert it inside a ${} block.

Conclusion

That’s all folks! In this article, you learned what are various operators provided in Kotlin to perform operations on Numbers, Booleans, and Strings. You also learned how the expressions containing operators are translated to function calls internally.

As always, Thank you for reading.