понедельник, 22 апреля 2024 г.

Generics in Java

    Generics in Java provide a way to create classes, interfaces, and methods that operate with types as parameters. They enable you to define classes and methods with placeholder types which are specified when the classes, interfaces, or methods are instantiated or called, respectively. 

There are mainly 3 advantages of generics. They are as follows:

1) Type-safety: We can hold only a single type of objects in generics.

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

public class Main {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
// Type safety ensures that only strings can be added to the list
// stringList.add(10); // This would cause a compilation error
for (String str : stringList) {
System.out.println(str.toUpperCase());
}
}
}


2) Type casting is not required: There is no need to typecast the object.

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

public class Main {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
// No need for type casting when retrieving elements from the list
for (String str : stringList) {
System.out.println(str.toUpperCase());
}
}
}


3) Compile-Time Checking: It is checked at compile time so problem will not occur at runtime. The good programming strategy says it is far better to handle the problem at compile time than runtime.

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

public class Main {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
// Type safety: Compiler ensures only String objects are added to the list
// stringList.add(10); // This would cause a compilation error
// Compile-time checking: Correct usage of generics
for (String str : stringList) {
System.out.println(str.toUpperCase());
}
// Compile-time checking: Incorrect usage of generics
// List<Integer> integerList = new ArrayList<String>(); // Compilation error
}
}


Generic class

A class that can refer to any type is known as a generic class. Here, we are using the T type parameter to create the generic class of specific type.

public class Box<T> {
private T value;

public Box(T value) {
this.value = value;
}

public T getValue() {
return value;
}

public void setValue(T value) {
this.value = value;
}

public static void main(String[] args) {
// Create a Box for Integer
Box<Integer> integerBox = new Box<>(10);
System.out.println("Integer Value: " + integerBox.getValue());

// Create a Box for String
Box<String> stringBox = new Box<>("Hello");
System.out.println("String Value: " + stringBox.getValue());
}
}

Type Parameters

The type parameters naming conventions are important to learn generics thoroughly. The common type parameters are as follows:

  1. T - Type
  2. E - Element
  3. K - Key
  4. N - Number
  5. V - Value
Generic Method

Like the generic class, we can create a generic method that can accept any type of arguments. Here, the scope of arguments is limited to the method where it is declared. It allows static as well as non-static methods.

public class TestGenerics4 {
// Generic method printArray with type parameter E
public static <E> void printArray(E[] elements) {
for (E element : elements) {
System.out.println(element);
}
System.out.println();
}
public static void main(String args[]) {
// Integer array
Integer[] intArray = {10, 20, 30, 40, 50};
// Character array
Character[] charArray = {'J', 'A', 'V', 'A', 'T', 'P', 'O', 'I', 'N', 'T'};
System.out.println("Printing Integer Array");
printArray(intArray); // Invoking printArray with Integer array
System.out.println("Printing Character Array");
printArray(charArray); // Invoking printArray with Character array
}
}

Wildcard in Generics

Wildcard is a special symbol used in type declarations to represent an unknown type. Wildcards are denoted by the ? character and are used to make generic types more flexible.

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

public class UnboundedWildcardExample {
// Method that prints elements of a list with unbounded wildcard
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}

public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);

List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");

System.out.println("Printing integerList:");
printList(integerList);

System.out.println("\nPrinting stringList:");
printList(stringList);
}
}


An upper bounded wildcard in Java generics, denoted by ? extends T, allows you to specify that a generic type can be any subtype of a particular type T or T itself. This means that the generic type can be any type that is a subclass of T. Upper bounded wildcards are useful when you want to make your code more flexible by accepting collections of any type that is a subtype of a particular class or interface.

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

public class UpperBoundedWildcardExample {
// Method that computes the sum of numbers in a list with an upper bounded wildcard
public static double sumOfNumbers(List<? extends Number> list) {
double sum = 0;
for (Number num : list) {
sum += num.doubleValue();
}
return sum;
}

public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);

List<Double> doubleList = new ArrayList<>();
doubleList.add(2.5);
doubleList.add(3.7);
doubleList.add(4.2);

System.out.println("Sum of integers: " + sumOfNumbers(integerList));
System.out.println("Sum of doubles: " + sumOfNumbers(doubleList));
}
}

A lower bounded wildcard in Java generics, denoted by ? super T, allows you to specify that a generic type can be any supertype of a particular type T or T itself. This means that the generic type can be any type that is a superclass of T. Lower bounded wildcards are useful when you want to make your code more flexible by accepting collections of any type that is a supertype of a particular class or interface.

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

public class LowerBoundedWildcardExample {
// Method that adds elements to a list with a lower bounded wildcard
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
}

public static void main(String[] args) {
List<Object> objectList = new ArrayList<>();
objectList.add("Hello");
objectList.add(3.14);

System.out.println("Before adding integers:");
for (Object obj : objectList) {
System.out.println(obj);
}

addNumbers(objectList);

System.out.println("\nAfter adding integers:");
for (Object obj : objectList) {
System.out.println(obj);
}
}
}


Комментариев нет:

Отправить комментарий