02 August 2023

Generics In Java - learngreen.net

 

Generics In Java


     It would be useful if we could develop a single sort technique that could arrange the elements in an array of any kind that supports sorting, such as an array of integers, a string, or a collection of numbers. Using a single method declaration for a group of related methods or a single class declaration for a group of related types, Java Generic Methods and Generic Classes enable programmers to express. Additionally, generics offer compile-time type safety, enabling programmers to identify erroneous types at the compile stage. Writing a generic method for sorting an array of objects using the Java Generic notion would allow us to sort the array's elements using Integer arrays, Double arrays, String arrays, and so on.


The important points about generics in Java are as follows:

Generics use type parameters (such as,) to act as placeholders for particular types. When defining a generic class, method, or interface, angle brackets (" >") are used to define these type parameters.


Type Safety:- Generic classes and methods make sure the proper data types are utilized with them. The code becomes more resilient because there is no longer a requirement for explicit type casting and runtime type mistakes are avoided.


Code reusability is enhanced and redundant code is reduced when you utilize generics to create code that can be used with many kinds.


Collections Framework:- Generics are heavily used in Java's collections framework. Because it has general interfaces like List <E>, Set<E>, and Map<K, V> collections can be typed to particular data types.


Generics support wildcards, which are denoted by the symbol ? and offer flexibility when interacting with unidentified types or generic collections. Bounded types are used with wildcards to limit the kinds that can be utilized (e.g., <? extends Number>? ,<? super T).


Generic Methods:- In addition to generic classes, you may create generic methods that, depending on the type parameter you specify when calling the method, can operate on a variety of types.


Generic Classes:-

The type parameter section that follows the class name is the only distinction between a generic class declaration and a non-generic class declaration. The type parameter section of a generic class may include one or more type parameters that are separated by commas, just like generic methods. These classes are known as parameterized classes or parameterized types because they accept one or more arguments. A class that functions with several types is known as a generic class. After the class name, the type parameter is supplied inside the angle brackets.





 package Java;
 public class Vessel {
 // Content of the vessel	 
 private T content; 
 // Method to set the content of the vessel
 public void setContent(T content) {
 this.content = content;
 
  }
 // Method to get the content of the vessel
  public T getContent() {
  return content;
    }
  public static void main(String[] args) {
 /*  Create an instance of Vessel
  *  with type parameter String
  */
  Vessel vessel = new Vessel<>();
  // Set the content of the vessel to "Oil"
  vessel.setContent("Oil");
  /*  Retrieve and print the 
   *  content of the vessel
   */
  String content = vessel.getContent();
  System.out.println(content);
  // Output-> Oil
    }
  }

 Type Parameters


Naming Conventions for type parameters are mandatory to learn in generics which are as follows:


1. T - Type

2. E - Element

3. K - Key

4. N - Number

5. V - Value

S, U, V


Generic Methods:-

  A single generic method declaration that may be called with several sorts of arguments can be written. The compiler handles each method call properly based on the types of parameters given to the generic method. The guidelines for defining generic methods are as follows:-


Angle brackets ( and >) are used to separate the type parameter section from the method's return type in all generic method declarations. The return type for the following example is E.


Each section has one or more type parameters that are separated by commas. A type parameter, often known as a type variable, is an identifier that offers a broad type name.


The type parameters can be used to define the return type and act as placeholders for the real type arguments, which are the kinds of arguments passed to the generic method. Like any other method, the body of a generic method is declared.


Keep in mind that type parameters cannot represent primitive types (such as int, double, or char), only reference types.






  package Java;
 public class GNXM {
 // Create Generic Method
 public static  void data(T element) {
 System.out.println("Received data: " + element);
    }
 public static void main(String[] args) {
 /* Examples of using the generic
  * method with different data types
  */
  data(10);
  // Output -> Received data: 10
  data("Generics");
  // Output -> Received data: Generics
  data(3.14); 
  // Output -> Received data: 3.14

  /* You can also explicitly specify
   * the type when calling the method
   */
  GNXM.data(true);
  // Output: Received data: true
    }
 }

 Bounded Type Parameters:-

    You might occasionally want to limit the types that are permitted to be supplied to a type argument. One could only want to accept instances of Numbers or their subclasses for a method that operates on numbers, for instance. Bounded-type parameters are used for this. List the name of the type parameter, the extends keyword, the higher bound, and then the type parameter to be bound.

   You can limit the types that can be used as arguments for a generic class, method, or interface by using bounded type parameters in Java generics. You can restrict the types that can be used in place of the type parameter by providing boundaries for it.

Bounded type parameters come in two varieties:-

Upper Bound Wildcards are indicated by <? extends T>. Any class that is either of type T or a subclass of T may be used as the type parameter. When working with a group of objects that belong to the same superclass, this is helpful.




 package Java;
 import java.util.ArrayList;
 import java.util.List;
 public class UBW {
 /* Method to calculate
  * the sum of elements
  * in a list of Numbers	 
  */
 public static double
 sumOfList(List list) {
 // Initialize the variable to store the sum	 
 double sum = 0.0;
 for (Number element : list) {
 /* Convert the current element
  * to a double and add it to the sum	 
  */
 sum += element.doubleValue();
  }
 /* Return the final sum
  * of the elements in the list
  */
 return sum;
 }	 
 public static void main(String[] args) {
 List integerList = new ArrayList<>();
 integerList.add(5);
 integerList.add(10);
 integerList.add(15);
 double sumIntegerList = sumOfList(integerList);
 System.out.println
 ("Sum of Integer list: " + sumIntegerList);
 // Output-> Sum of Integer list: 30.0
  List doubleList = new ArrayList<>();
 doubleList.add(2.5);
 doubleList.add(3.5);
 doubleList.add(4.5);
 double sumDoubleList = sumOfList(doubleList);
 System.out.println
 ("Sum of Double list: " + sumDoubleList);
 // Output-> Sum of Double list: 10.5	 

	}
 }

 


 package Java;
 import java.util.List;
 import java.util.ArrayList;
 public class UBW2 {
 // Method to divide two integers
 int divide(int x, int y) {
        return x / y;
    }
  /*
   * Method overloading: 
   * Method to divide two doubles
   */
 double divide(double x, double y) {
  return x / y;
    }

    /*  Method to divide a list of numbers
     *  using upper bounded wildcard
     */
 double divideList(List numbers) {
 double result = 1.0;
 /* Iterate through each
  * element in the list of numbers
  */
 for (Number num : numbers) {
 /*
  * Divide the current element
  * by the result and update the result
  */
 result /= num.doubleValue();
        }
  /*
   * Return the final result of
   * dividing all elements in the list
   */
        return result;
    }
 public static void main(String[] args) {
 UBW2 obj = new UBW2();
 /* Call the int version
  * of divide method, result is 2 (8 / 4)
  */
 System.out.println(obj.divide(8, 4));
 /* Call the double version
  * of divide method, result is 4.0 (8.0 / 2.0)
  */
 System.out.println(obj.divide(8.0, 2.0));
 
 /* Example of divideList method
  * with a list of integers
  */
 List intList = new ArrayList<>();
 intList.add(2);
 intList.add(2);
 intList.add(2);
 System.out.println(obj.divideList(intList));

 /* Example of divideList
  * method with a list of doubles
  */
 List doubleList = new ArrayList<>();
 doubleList.add(0.5);
 doubleList.add(0.5);
 System.out.println(obj.divideList(doubleList));
 /* Output-> 2
  *          4.0
  *          0.125
  *          4.0
  */
 
    }
 }
 
 

 

Lower Bounded Wildcards are indicated by  <? super T>. It permits any class that is either of type T or a superclass of T to be the type parameter. When you need to work with a collection that includes both objects of a given type and its superclasses, this is helpful.






  package Java;
 public class MetallicVessel {
 // Content of the metallic vessel
 private T content;
 /* Method to set the content
  * of the metallic vessel
  */
 public void setContent(T content2) {
 this.content = content2;
    }
 /* Method to get the content
  * of the metallic vessel
  */
    public T getContent() {
        return content;
    }
  /*
   * Method to set the content of
   * the metallic vessel with lower
   * bounded wildcard
   */
 public void setLowerBoundedContent
 (MetallicVessel metallicVessel,String content) {
 metallicVessel.setContent(content);
    }
 public static void main(String[] args) {
 /* Create an instance of
  * MetallicVessel with type parameter String
  */
 MetallicVessel metallicVessel = new MetallicVessel<>();
 // Set the content of the metallic vessel to "Oil"
 metallicVessel.setContent("Oil");
 // Retrieve and print the content of the metallic vessel
 String content = metallicVessel.getContent();
 System.out.println(content);
 // Output-> Oil
 /* Example of using setLowerBoundedContent
  * method with lower bounded wildcard
  */
 MetallicVessel

 


 package Java;
 public class MetallicVessel {
 // Content of the metallic vessel
 private T content;
 /* Method to set the content
  * of the metallic vessel
  */
 public void setContent(T content) {
 this.content = content;
    }
 /* Method to get the
  * content of the metallic vessel
  */
 public T getContent() {
 return content;
    }
 /* Helper method to set the content
  * of the metallic vessel with lower
  * bounded wildcard
  */
 public static  void setLowerBoundedContent
 (MetallicVessel vessel, U content) {
 vessel.setContent(content);
    }
 /* Helper method to copy content
  * from one MetallicVessel to another
  */
 public static  void copyContent
 (MetallicVessel source,
 MetallicVessel target) {
 target.setContent(source.getContent());
    }
 public static void main(String[] args) {
 /* Create an instance of MetallicVessel
  * with type parameter String
  */
 MetallicVessel metallicVessel
 = new MetallicVessel<>();
 // Set the content of the metallic vessel to "Oil"
 metallicVessel.setContent("Oil");
 /* Retrieve and print the content
  * of the metallic vessel
  */
 String content = metallicVessel.getContent();
 System.out.println(content);
 // Output-> Oil
 /* Example of using setLowerBoundedContent
  * method with lower bounded wildcard
  */
 MetallicVessel

 Writing more adaptable and reusable code is made easier by the use of restricted type parameters. Lower-bounded wildcards are used when you need to add elements to a collection that accepts a given type and its supertypes and upper-bounded wildcards are used when you need to access methods or properties specific to a particular superclass.




No comments:

Post a Comment