Generics
Generics are a feature in Java that allows you to write classes, interfaces, and methods
with type parameters that are specified by the user when they are used. This provides a
way to create reusable code that can work with any object type while still providing
compile-time type safety.
Here’s a brief overview:
• Type Parameters: These are placeholders for the types that will be used by the
generic class or method. They are usually represented by single uppercase letters
like T, E, K, V, etc.
• Generic Class: A class that has one or more type parameters. For example, class
Box<T> is a generic class with one type parameter T.
• Generic Method: A method that has its own type parameters. For example, <T>
void printArray(T[] array) is a generic method.
• Bounded Type Parameters: You can limit the types that can be used as type
parameters. For example, <T extends Number> restricts T to be a subclass of
Number.
• Type Inference: The Java compiler can often infer the actual type parameters from
the context, making the code cleaner and easier to read.
class Box<T> {
private T content;
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}
In the Box class example provided, a constructor is not explicitly defined. In Java, if no
constructor is declared, the compiler automatically provides a default no-argument
constructor that does nothing but call the superclass’s no-argument constructor. This is
sufficient for simple classes where you don’t need to initialize any state when an object is
created.
However, you can certainly add a constructor to the Box class if you want to initialize the
content when creating a new Box object. Here’s how you could modify the Box class to
include a constructor:
class Box<T> {
private T content;
public Box(T content) {
this.content = content;
}
public void set(T content) {
this.content = content;
}
public T get() {
return content;
}
}
Example 1. Generic Class:
The PetOwner class is designed to encapsulate details about a pet owner. It includes a
specific attribute, ownerName, which is of type String. Additionally, it has a generic
instance variable, pet, represented by the placeholder P. This design allows the type of pet
to be flexible, meaning that when we instantiate a PetOwner object, we can specify the
type of pet it refers to:
class PetOwner<P> {
private String ownerName;
private P pet;
public PetOwner(String ownerName, P pet) {
this.ownerName = ownerName;
this.pet = pet;
}
public String getOwnerName() {
return ownerName;
}
public P getPet() {
return pet;
}
}
class Dog {
// Dog class implementation
}
class Cat {
// Cat class implementation
}
public class Main {
public static void main(String[] args) {
PetOwner<Dog> dogOwner = new PetOwner<>("John", new Dog());
String ownerName = dogOwner.getOwnerName(); // Returns "John"
Dog dog = dogOwner.getPet(); // Returns the Dog object
}
}
Example 2. Generic Class:
The Triple class is a generic class that can hold three objects of potentially different
types simultaneously. It uses three type parameters, T, U, and V, which represent the types
of these three objects. Here’s a breakdown of its structure and functionality:
• Attributes: The class has three private attributes, first, second, and third, each
corresponding to one of the type parameters.
• Constructor: The constructor Triple(T first, U second, V third)
initializes the attributes with the provided objects.
• Getters: There are three getter methods, getFirst(), getSecond(), and
getThird(), which return the values of the respective attributes.
This class is useful when you need to associate three related pieces of data without
creating a specific class for them. For example, you could use it to store a combination of a
name, age, and email address together.
public class Triple<T, U, V> {
private T first;
private U second;
private V third;
public Triple(T first, U second, V third) {
this.first = first;
this.second = second;
this.third = third;
}
public T getFirst() {
return first;
}
public U getSecond() {
return second;
}
public V getThird() {
return third;
}
}
Task 1: Create a Generic Class
• Instructions:
1. Define a new class named Container.
2. Use angle brackets <> to specify the type parameter, like Container<T>.
3. Inside the class, declare a private variable of type T.
4. Write a public method named set to set the value of the variable.
5. Write another public method named get to return the value.
• Hints: Remember that T is just a placeholder for the actual data type that will be
used when an instance of the class is created.
Task 2: Create a Generic Pair Class
Instructions:
1. Define a new class named Pair.
2. Use angle brackets <> to specify two type parameters, like Pair<K, V>.
3. Inside the class, declare two private variables, one of type K and one of type V.
4. Write a constructor that initializes both variables.
5. Write public methods named getKey and getValue to return the respective values.
Hints:
• The K and V type parameters represent the types of the key-value pair respectively.
• This generic class can be used to store pairs of objects, like an entry in a map.
ArrayList
An ArrayList is like an array, but without the fixed size. You can add items, remove items,
and query the size of the list at runtime. Here are some key features:
• Dynamic Resizing: Unlike regular arrays, ArrayLists can automatically resize
themselves when elements are added or removed.
• Type Safety: ArrayLists can be generic, allowing you to specify the type of
elements they can contain, providing compile-time type safety.
• Random Access: You can access elements in constant time using an index, just
like with an array.
• Ordering: Elements in an ArrayList are ordered and the insertion order is
maintained.
• Duplicate Elements: ArrayLists allow duplicate elements and null values.
Here’s a simple example of creating and using an ArrayList:
ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
String fruit = list.get(1); // Returns "Banana"
In the example, we create an ArrayList that holds strings and add some fruits to it.
Task 1: Basic ArrayList Operations
• Instructions:
1. Create an ArrayList<String> object.
2. Use the add method to insert names.
3. Use the remove method with an index to remove a name.
4. Use the set method with an index and new name to replace a name.
5. Iterate over the list using a loop to print all names.
• Hints: The index in an ArrayList starts at zero.
Task 2: ArrayList with Custom Objects
• Instructions:
1. Define a class named Student with fields like name and age.
2. Create getter and setter methods for each field.
3. Instantiate an ArrayList<Student> object.
4. Create new Student objects and add them to your list using the add method.
5. Manipulate your list using methods like size, get, set, add, and remove.
• Hints: Remember that you can store any object in an ArrayList, but you should
specify the type for type safety.
By following these instructions and hints, you should be able to complete each task
successfully and understand how generics and ArrayList work in Java.