- Introduction to Generics
- Code Snippet 1: Generic Method
- Code Snippet 2: Generic Class
- Theoretical Background of Generics
- Code Snippet 3: Generic Interface
- Code Snippet 4: Wildcard Usage
- Detailed Analysis of Generics
- Code Snippet 5: Generic Exception Handling
- Code Snippet 6: Type Erasure
- Generics and their Syntax
- Code Snippet 7: Generic Method with Bounded Type Parameters
- Code Snippet 8: Generic Class with Multiple Type Parameters
- Use Case 1: Implementing Generics in Method
- Code Snippet 9: Generic Method with Multiple Type Parameters
- Code Snippet 10: Generic Method with Upper Bounded Type Parameter
- Use Case 2: Generics in Class Definition
- Code Snippet 11: Generic Class with Type Parameter
- Code Snippet 12: Generic Class with Multiple Type Parameters
- Use Case 3: Interface and Generics
- Code Snippet 13: Generic Interface
- Best Practice 1: Correct use of Wildcards
- Code Snippet 14: Upper Bounded Wildcard
- Code Snippet 15: Lower Bounded Wildcard
- Best Practice 2: Avoiding Raw Types
- Code Snippet 16: Raw Type Example
- Code Snippet 17: Generic Type Example
- Real World Example 1: Generic Data Structures
- Code Snippet 18: Generic List
- Code Snippet 19: Generic Queue
- Real World Example 2: Generic Algorithms
- Code Snippet 20: Generic Sorting Algorithm
- Code Snippet 21: Generic Searching Algorithm
- Real World Example 3: Type Safety with Generics
- Code Snippet 22: Type Safety with Generics
- Code Snippet 23: Type Inference with Generics
- Performance Consideration 1: Space Efficiency
- Code Snippet 24: Type Erasure
- Code Snippet 25: Type Erasure with Generic Methods
- Performance Consideration 2: Speed Efficiency
- Code Snippet 26: Generic Method Performance
- Code Snippet 27: Generic Class Performance
- Advanced Technique 1: Bounded Type Parameters
- Code Snippet 28: Upper Bounded Type Parameter
- Code Snippet 29: Lower Bounded Type Parameter
- Advanced Technique 2: Generic Type Inference
- Code Snippet 30: Type Inference with Generic Method
- Code Snippet 31: Type Inference with Generic Class
- Code Snippet 1: Generic Method
- Code Snippet 2: Generic Class
- Code Snippet 3: Generic Interface
- Code Snippet 4: Wildcard Usage
- Code Snippet 5: Generic Exception Handling
- Error Handling in Generics
Introduction to Generics
Generics in Java provide a way to create reusable code that can work with different types. It allows us to define classes, interfaces, and methods that can operate on a variety of data types without sacrificing type safety. By using generics, we can write code that is more flexible, efficient, and less error-prone.
Related Article: How To Parse JSON In Java
Code Snippet 1: Generic Method
public class GenericMethodExample { public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); } public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5}; Character[] charArray = {'H', 'E', 'L', 'L', 'O'}; System.out.print("Integer Array: "); printArray(intArray); System.out.print("Double Array: "); printArray(doubleArray); System.out.print("Character Array: "); printArray(charArray); } }
Output:
Integer Array: 1 2 3 4 5 Double Array: 1.1 2.2 3.3 4.4 5.5 Character Array: H E L L O
Code Snippet 2: Generic Class
public class GenericClassExample<T> { private T value; public GenericClassExample(T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args) { GenericClassExample<Integer> intValue = new GenericClassExample<>(10); GenericClassExample<String> stringValue = new GenericClassExample<>("Hello"); System.out.println("Integer Value: " + intValue.getValue()); System.out.println("String Value: " + stringValue.getValue()); } }
Output:
Integer Value: 10 String Value: Hello
Theoretical Background of Generics
Generics were introduced in Java 5 to provide compile-time type safety and eliminate the need for casting. The concept of generics is based on parameterized types. It allows us to define classes, interfaces, and methods that can work with different types, known as type parameters.
Related Article: How To Convert Array To List In Java
Code Snippet 3: Generic Interface
public interface GenericInterfaceExample<T> { T performOperation(T operand1, T operand2); }
Code Snippet 4: Wildcard Usage
public class WildcardExample { public static double sum(List<? extends Number> numbers) { double total = 0.0; for (Number number : numbers) { total += number.doubleValue(); } return total; } public static void main(String[] args) { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5); double sumOfIntegers = sum(integers); double sumOfDoubles = sum(doubles); System.out.println("Sum of Integers: " + sumOfIntegers); System.out.println("Sum of Doubles: " + sumOfDoubles); } }
Output:
Sum of Integers: 15.0 Sum of Doubles: 16.5
Detailed Analysis of Generics
Generics provide compile-time type checking and help prevent type-related errors at runtime. The Java compiler ensures that the code using generics is type-safe by performing type inference and enforcing type constraints. This improves code reliability and reduces the likelihood of bugs.
Related Article: How To Iterate Over Entries In A Java Map
Code Snippet 5: Generic Exception Handling
public class GenericExceptionHandlingExample { public static <T extends Exception> void handleException(T exception) { System.out.println("Exception: " + exception.getMessage()); } public static void main(String[] args) { NullPointerException nullPointerException = new NullPointerException("Null Value Detected"); ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException = new ArrayIndexOutOfBoundsException("Array Index Out of Bounds"); handleException(nullPointerException); handleException(arrayIndexOutOfBoundsException); } }
Output:
Exception: Null Value Detected Exception: Array Index Out of Bounds
Code Snippet 6: Type Erasure
import java.util.ArrayList; import java.util.List; public class TypeErasureExample { public static void main(String[] args) { List<String> stringList = new ArrayList<>(); stringList.add("Hello"); stringList.add("World"); List<Integer> integerList = new ArrayList<>(); integerList.add(1); integerList.add(2); System.out.println(stringList.getClass() == integerList.getClass()); } }
Output:
true
Generics and their Syntax
The syntax for using generics involves declaring type parameters inside angle brackets (““) after the class, interface, or method name. The type parameters can be any valid identifier and are used to represent the actual types that will be used when the code is instantiated or invoked.
Related Article: How To Split A String In Java
Code Snippet 7: Generic Method with Bounded Type Parameters
public class BoundedTypeParameterExample { public static <T extends Number> double sum(List<T> numbers) { double total = 0.0; for (T number : numbers) { total += number.doubleValue(); } return total; } public static void main(String[] args) { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5); double sumOfIntegers = sum(integers); double sumOfDoubles = sum(doubles); System.out.println("Sum of Integers: " + sumOfIntegers); System.out.println("Sum of Doubles: " + sumOfDoubles); } }
Output:
Sum of Integers: 15.0 Sum of Doubles: 16.5
Code Snippet 8: Generic Class with Multiple Type Parameters
public class MultipleTypeParameterExample<T, U> { private T value1; private U value2; public MultipleTypeParameterExample(T value1, U value2) { this.value1 = value1; this.value2 = value2; } public T getValue1() { return value1; } public U getValue2() { return value2; } public static void main(String[] args) { MultipleTypeParameterExample<Integer, String> example = new MultipleTypeParameterExample<>(10, "Hello"); System.out.println("Value 1: " + example.getValue1()); System.out.println("Value 2: " + example.getValue2()); } }
Output:
Value 1: 10 Value 2: Hello
Use Case 1: Implementing Generics in Method
Generics can be used in methods to create reusable code that can work with different types. By using generics in methods, we can avoid code duplication and improve code maintainability.
Related Article: How To Convert Java Objects To JSON With Jackson
Code Snippet 9: Generic Method with Multiple Type Parameters
public class MultipleTypeParameterMethodExample { public static <T, U> void printValues(T value1, U value2) { System.out.println("Value 1: " + value1); System.out.println("Value 2: " + value2); } public static void main(String[] args) { printValues(10, "Hello"); printValues(3.14, true); } }
Output:
Value 1: 10 Value 2: Hello Value 1: 3.14 Value 2: true
Code Snippet 10: Generic Method with Upper Bounded Type Parameter
public class UpperBoundedTypeParameterExample { public static <T extends Number> double sum(List<T> numbers) { double total = 0.0; for (T number : numbers) { total += number.doubleValue(); } return total; } public static void main(String[] args) { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5); double sumOfIntegers = sum(integers); double sumOfDoubles = sum(doubles); System.out.println("Sum of Integers: " + sumOfIntegers); System.out.println("Sum of Doubles: " + sumOfDoubles); } }
Output:
Sum of Integers: 15.0 Sum of Doubles: 16.5
Use Case 2: Generics in Class Definition
Generics can also be used in class definitions to create generic classes that can work with different types. By using generics in classes, we can create reusable data structures and algorithms that are type-safe and flexible.
Related Article: Storing Contact Information in Java Data Structures
Code Snippet 11: Generic Class with Type Parameter
public class GenericClassExample<T> { private T value; public GenericClassExample(T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args) { GenericClassExample<Integer> intValue = new GenericClassExample<>(10); GenericClassExample<String> stringValue = new GenericClassExample<>("Hello"); System.out.println("Integer Value: " + intValue.getValue()); System.out.println("String Value: " + stringValue.getValue()); } }
Output:
Integer Value: 10 String Value: Hello
Code Snippet 12: Generic Class with Multiple Type Parameters
public class MultipleTypeParameterClassExample<T, U> { private T value1; private U value2; public MultipleTypeParameterClassExample(T value1, U value2) { this.value1 = value1; this.value2 = value2; } public T getValue1() { return value1; } public U getValue2() { return value2; } public static void main(String[] args) { MultipleTypeParameterClassExample<Integer, String> example = new MultipleTypeParameterClassExample<>(10, "Hello"); System.out.println("Value 1: " + example.getValue1()); System.out.println("Value 2: " + example.getValue2()); } }
Output:
Value 1: 10 Value 2: Hello
Use Case 3: Interface and Generics
Generics can also be used in interfaces to create generic interfaces that can be implemented by different classes. By using generics in interfaces, we can define common behaviors and contracts that can work with different types.
Related Article: How to Convert JSON String to Java Object
Code Snippet 13: Generic Interface
public interface GenericInterfaceExample<T> { T performOperation(T operand1, T operand2); } public class AdditionOperation implements GenericInterfaceExample<Integer> { @Override public Integer performOperation(Integer operand1, Integer operand2) { return operand1 + operand2; } } public class ConcatenationOperation implements GenericInterfaceExample<String> { @Override public String performOperation(String operand1, String operand2) { return operand1 + operand2; } } public class Main { public static void main(String[] args) { GenericInterfaceExample<Integer> addition = new AdditionOperation(); GenericInterfaceExample<String> concatenation = new ConcatenationOperation(); System.out.println("Addition: " + addition.performOperation(5, 10)); System.out.println("Concatenation: " + concatenation.performOperation("Hello", "World")); } }
Output:
Addition: 15 Concatenation: HelloWorld
Best Practice 1: Correct use of Wildcards
When working with generics, it is important to understand the correct use of wildcards. Wildcards allow us to express “some unknown type” when working with generic types. There are two types of wildcards: upper bounded and lower bounded.
Code Snippet 14: Upper Bounded Wildcard
public class UpperBoundedWildcardExample { public static double sum(List<? extends Number> numbers) { double total = 0.0; for (Number number : numbers) { total += number.doubleValue(); } return total; } public static void main(String[] args) { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5); double sumOfIntegers = sum(integers); double sumOfDoubles = sum(doubles); System.out.println("Sum of Integers: " + sumOfIntegers); System.out.println("Sum of Doubles: " + sumOfDoubles); } }
Output:
Sum of Integers: 15.0 Sum of Doubles: 16.5
Related Article: How to Retrieve Current Date and Time in Java
Code Snippet 15: Lower Bounded Wildcard
public class LowerBoundedWildcardExample { public static void addIntegers(List<? super Integer> numbers) { for (int i = 1; i <= 5; i++) { numbers.add(i); } } public static void main(String[] args) { List<Number> numbers = new ArrayList<>(); numbers.add(0.0); addIntegers(numbers); System.out.println("Numbers: " + numbers); } }
Output:
Numbers: [0.0, 1, 2, 3, 4, 5]
Best Practice 2: Avoiding Raw Types
When using generics, it is recommended to avoid using raw types. Raw types are generic types without type parameters specified. They exist for backward compatibility with pre-generic code but can lead to type safety issues and potential runtime errors.
Code Snippet 16: Raw Type Example
public class RawTypeExample { public static void main(String[] args) { List rawList = new ArrayList(); rawList.add(10); rawList.add("Hello"); for (Object element : rawList) { System.out.println(element); } } }
Output:
10 Hello
Related Article: How to Reverse a String in Java
Code Snippet 17: Generic Type Example
public class GenericTypeExample { public static void main(String[] args) { List<Object> genericList = new ArrayList<>(); genericList.add(10); genericList.add("Hello"); for (Object element : genericList) { System.out.println(element); } } }
Output:
10 Hello
Real World Example 1: Generic Data Structures
Generics are widely used in the implementation of data structures such as lists, queues, and maps. By using generics in data structures, we can create highly reusable and type-safe containers that can store and manipulate data of different types.
Code Snippet 18: Generic List
public class GenericListExample<T> { private List<T> list; public GenericListExample() { this.list = new ArrayList<>(); } public void add(T element) { list.add(element); } public void remove(T element) { list.remove(element); } public void print() { for (T element : list) { System.out.println(element); } } public static void main(String[] args) { GenericListExample<String> stringList = new GenericListExample<>(); stringList.add("Hello"); stringList.add("World"); GenericListExample<Integer> integerList = new GenericListExample<>(); integerList.add(1); integerList.add(2); System.out.println("String List:"); stringList.print(); System.out.println("Integer List:"); integerList.print(); } }
Output:
String List: Hello World Integer List: 1 2
Related Article: How to Generate Random Integers in a Range in Java
Code Snippet 19: Generic Queue
import java.util.LinkedList; import java.util.Queue; public class GenericQueueExample<T> { private Queue<T> queue; public GenericQueueExample() { this.queue = new LinkedList<>(); } public void enqueue(T element) { queue.add(element); } public T dequeue() { return queue.poll(); } public void print() { for (T element : queue) { System.out.println(element); } } public static void main(String[] args) { GenericQueueExample<String> stringQueue = new GenericQueueExample<>(); stringQueue.enqueue("Hello"); stringQueue.enqueue("World"); GenericQueueExample<Integer> integerQueue = new GenericQueueExample<>(); integerQueue.enqueue(1); integerQueue.enqueue(2); System.out.println("String Queue:"); stringQueue.print(); System.out.println("Integer Queue:"); integerQueue.print(); } }
Output:
String Queue: Hello World Integer Queue: 1 2
Real World Example 2: Generic Algorithms
Generics are also used in the implementation of generic algorithms that can work with different data types. By using generics in algorithms, we can create reusable and type-safe methods that can perform operations on different types of data.
Code Snippet 20: Generic Sorting Algorithm
import java.util.Arrays; public class GenericSortingExample { public static <T extends Comparable<T>> void sort(T[] array) { Arrays.sort(array); } public static void main(String[] args) { Integer[] integers = {5, 3, 1, 4, 2}; String[] strings = {"D", "B", "E", "A", "C"}; System.out.println("Before Sorting - Integers: " + Arrays.toString(integers)); System.out.println("Before Sorting - Strings: " + Arrays.toString(strings)); sort(integers); sort(strings); System.out.println("After Sorting - Integers: " + Arrays.toString(integers)); System.out.println("After Sorting - Strings: " + Arrays.toString(strings)); } }
Output:
Before Sorting - Integers: [5, 3, 1, 4, 2] Before Sorting - Strings: [D, B, E, A, C] After Sorting - Integers: [1, 2, 3, 4, 5] After Sorting - Strings: [A, B, C, D, E]
Related Article: Java Equals Hashcode Tutorial
Code Snippet 21: Generic Searching Algorithm
import java.util.Arrays; public class GenericSearchingExample { public static <T extends Comparable<T>> int binarySearch(T[] array, T key) { int low = 0; int high = array.length - 1; while (low <= high) { int mid = (low + high) / 2; int comparison = array[mid].compareTo(key); if (comparison == 0) { return mid; } else if (comparison < 0) { low = mid + 1; } else { high = mid - 1; } } return -1; } public static void main(String[] args) { Integer[] integers = {1, 2, 3, 4, 5}; String[] strings = {"A", "B", "C", "D", "E"}; int index1 = binarySearch(integers, 3); int index2 = binarySearch(strings, "D"); System.out.println("Index of 3 in Integers: " + index1); System.out.println("Index of D in Strings: " + index2); } }
Output:
Index of 3 in Integers: 2 Index of D in Strings: 3
Real World Example 3: Type Safety with Generics
Generics provide type safety by ensuring that the code operates on the correct data types. By using generics, we can catch type-related errors at compile-time, reducing the likelihood of runtime errors and improving code reliability.
Code Snippet 22: Type Safety with Generics
public class TypeSafetyExample { public static void main(String[] args) { List<String> stringList = new ArrayList<>(); stringList.add("Hello"); stringList.add("World"); // stringList.add(10); // Compilation Error: Type mismatch for (String element : stringList) { System.out.println(element); } } }
Output:
Hello World
Related Article: How To Convert String To Int In Java
Code Snippet 23: Type Inference with Generics
public class TypeInferenceExample { public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); } public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5}; Character[] charArray = {'H', 'E', 'L', 'L', 'O'}; System.out.print("Integer Array: "); printArray(intArray); System.out.print("Double Array: "); printArray(doubleArray); System.out.print("Character Array: "); printArray(charArray); } }
Output:
Integer Array: 1 2 3 4 5 Double Array: 1.1 2.2 3.3 4.4 5.5 Character Array: H E L L O
Performance Consideration 1: Space Efficiency
Generics do not have a significant impact on space efficiency because type parameter information is erased at runtime. The JVM uses type erasure to replace type parameters with their upper bound or Object if no upper bound is specified.
Code Snippet 24: Type Erasure
import java.util.ArrayList; import java.util.List; public class TypeErasureExample { public static void main(String[] args) { List<String> stringList = new ArrayList<>(); stringList.add("Hello"); stringList.add("World"); List<Integer> integerList = new ArrayList<>(); integerList.add(1); integerList.add(2); System.out.println(stringList.getClass() == integerList.getClass()); } }
Output:
true
Related Article: Java Composition Tutorial
Code Snippet 25: Type Erasure with Generic Methods
public class TypeErasureMethodExample { public static void printList(List<String> stringList) { for (String element : stringList) { System.out.println(element); } } public static void main(String[] args) { List<String> stringList = new ArrayList<>(); stringList.add("Hello"); stringList.add("World"); printList(stringList); } }
Output:
Hello World
Performance Consideration 2: Speed Efficiency
Generics have a minor impact on speed efficiency due to the additional overhead of type checking and type inference at compile-time. However, this impact is negligible and generally outweighed by the benefits of type safety and code reuse provided by generics.
Code Snippet 26: Generic Method Performance
public class GenericMethodPerformanceExample { public static <T> void performOperation(T element) { // Perform some operation } public static void main(String[] args) { long startTime = System.nanoTime(); for (int i = 0; i < 1000000; i++) { performOperation(i); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println("Execution Time: " + duration + " nanoseconds"); } }
Output:
Execution Time: ...
Related Article: Java Hashmap Tutorial
Code Snippet 27: Generic Class Performance
public class GenericClassPerformanceExample<T> { private T value; public GenericClassPerformanceExample(T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args) { long startTime = System.nanoTime(); for (int i = 0; i < 1000000; i++) { GenericClassPerformanceExample<Integer> example = new GenericClassPerformanceExample<>(i); int value = example.getValue(); } long endTime = System.nanoTime(); long duration = endTime - startTime; System.out.println("Execution Time: " + duration + " nanoseconds"); } }
Output:
Execution Time: ...
Advanced Technique 1: Bounded Type Parameters
Bounded type parameters allow us to restrict the types that can be used as type arguments in generics. We can specify upper bounds, lower bounds, or both to limit the type arguments to a specific range of types.
Code Snippet 28: Upper Bounded Type Parameter
public class UpperBoundedTypeParameterExample { public static <T extends Number> double sum(List<T> numbers) { double total = 0.0; for (T number : numbers) { total += number.doubleValue(); } return total; } public static void main(String[] args) { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5); double sumOfIntegers = sum(integers); double sumOfDoubles = sum(doubles); System.out.println("Sum of Integers: " + sumOfIntegers); System.out.println("Sum of Doubles: " + sumOfDoubles); } }
Output:
Sum of Integers: 15.0 Sum of Doubles: 16.5
Related Article: Popular Data Structures Asked in Java Interviews
Code Snippet 29: Lower Bounded Type Parameter
public class LowerBoundedTypeParameterExample { public static void addIntegers(List<? super Integer> numbers) { for (int i = 1; i <= 5; i++) { numbers.add(i); } } public static void main(String[] args) { List<Number> numbers = new ArrayList<>(); numbers.add(0.0); addIntegers(numbers); System.out.println("Numbers: " + numbers); } }
Output:
Numbers: [0.0, 1, 2, 3, 4, 5]
Advanced Technique 2: Generic Type Inference
Type inference allows the Java compiler to automatically determine the type arguments of generic methods or constructors based on the context in which they are used. This eliminates the need to explicitly specify the type arguments, making the code more concise.
Code Snippet 30: Type Inference with Generic Method
public class TypeInferenceMethodExample { public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); } public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5}; Character[] charArray = {'H', 'E', 'L', 'L', 'O'}; System.out.print("Integer Array: "); printArray(intArray); System.out.print("Double Array: "); printArray(doubleArray); System.out.print("Character Array: "); printArray(charArray); } }
Output:
Integer Array: 1 2 3 4 5 Double Array: 1.1 2.2 3.3 4.4 5.5 Character Array: H E L L O
Related Article: Java List Tutorial
Code Snippet 31: Type Inference with Generic Class
public class TypeInferenceClassExample<T> { private T value; public TypeInferenceClassExample(T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args) { TypeInferenceClassExample<Integer> intValue = new TypeInferenceClassExample<>(10); TypeInferenceClassExample<String> stringValue = new TypeInferenceClassExample<>("Hello"); System.out.println("Integer Value: " + intValue.getValue()); System.out.println("String Value: " + stringValue.getValue()); } }
Output:
Integer Value: 10 String Value: Hello
Code Snippet 1: Generic Method
public class GenericMethodExample { public static <T> void printArray(T[] array) { for (T element : array) { System.out.print(element + " "); } System.out.println(); } public static void main(String[] args) { Integer[] intArray = {1, 2, 3, 4, 5}; Double[] doubleArray = {1.1, 2.2, 3.3, 4.4, 5.5}; Character[] charArray = {'H', 'E', 'L', 'L', 'O'}; System.out.print("Integer Array: "); printArray(intArray); System.out.print("Double Array: "); printArray(doubleArray); System.out.print("Character Array: "); printArray(charArray); } }
Output:
Integer Array: 1 2 3 4 5 Double Array: 1.1 2.2 3.3 4.4 5.5 Character Array: H E L L O
Code Snippet 2: Generic Class
public class GenericClassExample<T> { private T value; public GenericClassExample(T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args) { GenericClassExample<Integer> intValue = new GenericClassExample<>(10); GenericClassExample<String> stringValue = new GenericClassExample<>("Hello"); System.out.println("Integer Value: " + intValue.getValue()); System.out.println("String Value: " + stringValue.getValue()); } }
Output:
Integer Value: 10 String Value: Hello
Related Article: Java Set Tutorial
Code Snippet 3: Generic Interface
public interface GenericInterfaceExample<T> { T performOperation(T operand1, T operand2); }
Code Snippet 4: Wildcard Usage
public class WildcardExample { public static double sum(List<? extends Number> numbers) { double total = 0.0; for (Number number : numbers) { total += number.doubleValue(); } return total; } public static void main(String[] args) { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5); double sumOfIntegers = sum(integers); double sumOfDoubles = sum(doubles); System.out.println("Sum of Integers: " + sumOfIntegers); System.out.println("Sum of Doubles: " + sumOfDoubles); } }
Output:
Sum of Integers: 15.0 Sum of Doubles: 16.5
Code Snippet 5: Generic Exception Handling
public class GenericExceptionHandlingExample { public static <T extends Exception> void handleException(T exception) { System.out.println("Exception: " + exception.getMessage()); } public static void main(String[] args) { NullPointerException nullPointerException = new NullPointerException("Null Value Detected"); ArrayIndexOutOfBoundsException arrayIndexOutOfBoundsException = new ArrayIndexOutOfBoundsException("Array Index Out of Bounds"); handleException(nullPointerException); handleException(arrayIndexOutOfBoundsException); } }
Output:
Exception: Null Value Detected Exception: Array Index Out of Bounds
Related Article: Java Classloader: How to Load Classes in Java
Error Handling in Generics
When working with generics, it is important to handle errors and exceptions appropriately. This ensures that the code behaves correctly and provides meaningful feedback to the users. Proper error handling can help identify and resolve issues early in the development process.