StackStalk
  • Home
  • Java
    • Java Collection
    • Spring Boot Collection
  • Python
    • Python Collection
  • C++
    • C++ Collection
    • Progamming Problems
    • Algorithms
    • Data Structures
    • Design Patterns
  • General
    • Tips and Tricks

Friday, July 18, 2014

Generics in Java

 July 18, 2014     Java     No comments   

Generics is a key element in the Java programming language and a solid understanding is essential for any Java programmer. This article provides an introduction to the concepts around Generics in Java.

Generics Introduction

Generics provide the ability to create classes, methods and interfaces to be used with different types of data in a type-safe manner. Generics by adding type safety to the code enables bugs to be detected at compile time rather than at run time. This provides stability to the implementation. The Collections Framework is a great example for understanding Generics. Collections classes like Lists and Maps can used in a type-safe manner by using Generics.

Generalized classes with Object type

Java has the capability to create generalized classes, method and interfaces by using Object types. Let us consider a quick example of creating a stack class using Object type. In this example, we use an ArrayList of objects to hold the data.
package com.sourcetricks.generics;

import java.util.ArrayList;
import java.util.List;

public class ObjectStack {
   private List<Object> data = new ArrayList<Object>();
 
   public void push(Object item) {
     data.add(item);
   }
 
   public Object pop() {
     if ( data.size() > 0 ) {
        Object val = data.get(data.size()-1);
        data.remove(data.size()-1);
        return val;
     }
     return null;
   }
}
To use this stack our main would something like below. Please note that we need to do the type casting every time we pop an element from the stack. Assuming the intent is to create a stack of String if someone my mistake puts in a integer type then it would result in a ClassCastException. Note that it is not possible to detect these mistakes at compile time. Run-time issues are hard to debug for large projects.
public class JavaGenerics {
   public static void main(String[] args) {
     ObjectStack st = new ObjectStack();
     st.push("string");
     st.push(10);
     int i = (Integer) st.pop();
     String s = (String) st.pop();   
  }
}

Generalized classes with Generic types

Generic types add the type safety that is lacking in the previous example. There is also no need to cast an Object type into the actual data type.

Let us redo the previous example using generic types. A generic class is defined with the format "class name {}". Here T1, T2 etc are the type parameters. Note that we have replaced all Object types with type parameter T in this example.

public class Stack<T> {
    private List<T> data = new ArrayList<T>();
 
    public void push(T item) {
       data.add(item);
    }
 
    public T pop() {
      if ( data.size() > 0 ) {
        T val = data.get(data.size()-1);
 data.remove(data.size()-1);
 return val;
      }
      return null;
    }
}
To use this stack our main would something like below. Please note when we instantiate the class we specify the concrete type to be used. If we try to add to the stack any type other than String then we would get a compile time error.
public class JavaGenerics {
   public static void main(String[] args) {
     Stack<String> st = new Stack<String>();
     st.push("string1");
     st.push("string2");
     String s1 = st.pop();
     String s2 = st.pop();   
   }
}

Raw types

A raw type is the name of a generic class or interface without any type arguments. We can rewrite our main as below. This approach again makes the code unsafe and using of raw types should be avoided.
public class JavaGenerics {
   public static void main(String[] args) {
     Stack st = new Stack();
     st.push("string1");
     st.push("string2");
     String s1 = (String) st.pop();
     String s2 = (String) st.pop();     
   }
}

The Diamond

In Java 7, we can replace the type arguments required to invoke the constructor with empty <>. The compiler infers the type from the context. Using <> is called diamond. We can rewrite the previous example as below.
public class JavaGenerics {
   public static void main(String[] args) {
     Stack<String> st = new Stack<>();
     st.push("string1");
     st.push("string2");
     String s1 = st.pop();
     String s2 = st.pop();   
   }
}

Generic methods

Generics can be used with methods as well. We can declare generic methods that uses one or more type parameters.
public static <T> boolean myfunc(T a, T b) {
  if ( a == b ) return true;
  return false;
}

Bounded type parameters

When specifying a type parameter we can specify an upper bound from which all type arguments should be derived. The previous example doesn't make sense for String type. We can restrict the method to be limited to Number type by specifying the superclass as Number.
public static <T extends Number> boolean myfunc(T a, T b) {
  if ( a == b ) return true;
  return false;
}
Email ThisBlogThis!Share to XShare to Facebook
Newer Post Older Post Home

0 comments:

Post a Comment

Follow @StackStalk
Get new posts by email:
Powered by follow.it

Popular Posts

  • Python FastAPI file upload and download
    In this article, we will look at an example of how to implement a file upload and download API in a Python FastAPI microservice. Example bel...
  • Monitor Spring Boot App with Micrometer and Prometheus
    Modern distributed applications typically have multiple microservices working together. Ability to monitor and manage aspects like health, m...
  • Avro Producer and Consumer with Python using Confluent Kafka
    In this article, we will understand Avro a popular data serialization format in streaming data applications and develop a simple Avro Produc...
  • Server-Sent Events with Spring WebFlux
    In this article we will review the concepts of server-sent events and work on an example using WebFlux. Before getting into this article it ...
  • Accessing the Kubernetes API
    In this article, we will explore the steps required to access the Kubernetes API and overcome common challenges. All operations and communic...
  • Python FastAPI microservice with Okta and OPA
    Authentication (AuthN) and Authorization (AuthZ) is a common challenge when developing microservices. In this article, we will explore how t...
  • Scheduling jobs in Python
    When developing applications and microservices we run into scenarios where there is a need to run scheduled tasks. Examples include performi...
  • Using Tekton to deploy KNative services
    Tekton is a popular open-source framework for building continuous delivery pipelines. Tekton provides a declarative way to define pipelines ...

Copyright © StackStalk