Practice Questions — Generics in Java
← Back to NotesTopic-Specific Questions
Question 1
Easy
What is the output?
class Box<T> {
T value;
Box(T v) { this.value = v; }
T get() { return value; }
}
public class Main {
public static void main(String[] args) {
Box<String> b = new Box<>("Hello");
System.out.println(b.get());
System.out.println(b.get().length());
}
}T is String. get() returns a String. No cast needed.
Hello5Question 2
Easy
What is the output?
class Pair<A, B> {
A first;
B second;
Pair(A a, B b) { first = a; second = b; }
}
public class Main {
public static void main(String[] args) {
Pair<String, Integer> p = new Pair<>("Arjun", 20);
System.out.println(p.first);
System.out.println(p.second);
System.out.println(p.first.getClass().getSimpleName());
System.out.println(p.second.getClass().getSimpleName());
}
}A=String, B=Integer. getClass().getSimpleName() returns the class name.
Arjun20StringIntegerQuestion 3
Easy
Does this code compile?
ArrayList<int> list = new ArrayList<>();Can generics use primitive types?
No, it does not compile. Compilation error: type argument cannot be a primitive type.
Question 4
Easy
What is the output?
public class Main {
static <T> T getFirst(T[] array) {
return array[0];
}
public static void main(String[] args) {
Integer[] ints = {10, 20, 30};
String[] strs = {"A", "B", "C"};
System.out.println(getFirst(ints));
System.out.println(getFirst(strs));
}
}The generic method infers T from the array type.
10AQuestion 5
Easy
What is the output?
ArrayList<String> a = new ArrayList<>();
ArrayList<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass());Due to type erasure, what are these at runtime?
trueQuestion 6
Medium
What is the output?
class MathBox<T extends Number> {
T value;
MathBox(T v) { value = v; }
double doubleVal() { return value.doubleValue(); }
}
public class Main {
public static void main(String[] args) {
MathBox<Integer> a = new MathBox<>(42);
MathBox<Double> b = new MathBox<>(3.14);
System.out.println(a.doubleVal());
System.out.println(b.doubleVal());
}
}T extends Number, so doubleValue() is available.
42.03.14Question 7
Medium
Does this code compile?
class Box<T> {
static T staticField; // Is this valid?
}Static fields belong to the class, not to instances. But each instance can have different T.
No, it does not compile. Compilation error: cannot make a static reference to the non-static type T.
Question 8
Medium
What is the output?
import java.util.List;
public class Main {
static double sum(List<? extends Number> list) {
double total = 0;
for (Number n : list) total += n.doubleValue();
return total;
}
public static void main(String[] args) {
System.out.println(sum(List.of(1, 2, 3)));
System.out.println(sum(List.of(1.5, 2.5)));
}
}? extends Number accepts List and List.
6.04.0Question 9
Medium
Does this code compile?
List<? extends Number> list = new ArrayList<Integer>();
list.add(42);Can you add elements to a List extends Number>?
No, it does not compile. Compilation error: cannot add Integer to List extends Number>.
Question 10
Medium
What is the output?
import java.util.ArrayList;
import java.util.List;
public class Main {
static void addInts(List<? super Integer> list) {
list.add(10);
list.add(20);
}
public static void main(String[] args) {
List<Number> nums = new ArrayList<>();
addInts(nums);
System.out.println(nums);
List<Object> objs = new ArrayList<>();
addInts(objs);
System.out.println(objs);
}
}? super Integer accepts List, List, and List
[10, 20][10, 20]Question 11
Hard
What is the output?
public class Main {
static <T extends Comparable<T>> T max(T a, T b, T c) {
T result = a;
if (b.compareTo(result) > 0) result = b;
if (c.compareTo(result) > 0) result = c;
return result;
}
public static void main(String[] args) {
System.out.println(max(3, 7, 2));
System.out.println(max("apple", "banana", "cherry"));
System.out.println(max(3.14, 2.71, 1.41));
}
}The method works for any Comparable type. String comparison is lexicographic.
7cherry3.14Question 12
Hard
Does this code compile?
class Container<T> {
T create() {
return new T(); // ?
}
}What does T become at runtime due to type erasure?
No, it does not compile. Compilation error: cannot instantiate the type T.
Question 13
Hard
What is the output?
import java.util.*;
public class Main {
static <T> void copy(List<? extends T> src, List<? super T> dst) {
for (T item : src) {
dst.add(item);
}
}
public static void main(String[] args) {
List<Integer> ints = List.of(1, 2, 3);
List<Object> objs = new ArrayList<>();
copy(ints, objs);
System.out.println(objs);
}
}src is a producer (extends), dst is a consumer (super). T is inferred.
[1, 2, 3]Question 14
Hard
What is the output?
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
strings.add("A");
strings.add("B");
List raw = strings; // Raw type
raw.add(100); // No compile error!
System.out.println(strings.size());
for (Object o : strings) {
System.out.println(o);
}
}
}Raw types bypass generic type checking. What happens when iterating?
3AB100Question 15
Hard
What is the output?
class Wrapper<T extends Comparable<T>> {
T value;
Wrapper(T v) { value = v; }
boolean isGreater(Wrapper<T> other) {
return value.compareTo(other.value) > 0;
}
}
public class Main {
public static void main(String[] args) {
Wrapper<Integer> a = new Wrapper<>(10);
Wrapper<Integer> b = new Wrapper<>(5);
Wrapper<String> c = new Wrapper<>("Hello");
Wrapper<String> d = new Wrapper<>("World");
System.out.println(a.isGreater(b));
System.out.println(b.isGreater(a));
System.out.println(c.isGreater(d));
}
}compareTo returns positive if this > other. String comparison is lexicographic.
truefalsefalseMixed & Application Questions
Question 1
Easy
What is the purpose of generics in Java?
Think about type safety and code reuse.
Generics provide compile-time type safety and eliminate the need for explicit casting. They allow writing classes and methods that work with any type while catching type errors at compile time instead of runtime. A single generic class (
Box<T>) replaces multiple type-specific classes.Question 2
Easy
What is type erasure?
Think about what happens to generic type information at runtime.
Type erasure is the process by which the Java compiler removes all generic type information during compilation. At runtime,
ArrayList<String> and ArrayList<Integer> are both just ArrayList. Generics are a compile-time-only feature.Question 3
Easy
What is the output?
class Container<T> {
T value;
Container(T v) { value = v; }
String getType() { return value.getClass().getSimpleName(); }
}
public class Main {
public static void main(String[] args) {
Container<Integer> a = new Container<>(42);
Container<String> b = new Container<>("Hi");
System.out.println(a.getType());
System.out.println(b.getType());
}
}getClass() works on the actual object at runtime.
IntegerStringQuestion 4
Medium
What is the difference between
List<Object> and List<?>?One accepts any object for add. The other is read-only.
List<Object> is a list that holds Objects. You can add any object to it. List<?> is a list of unknown type. You can only read from it (as Object) and cannot add to it (except null). List<String> IS-NOT a List<Object>, but it IS a List<?>.Question 5
Medium
Explain the PECS principle with an example.
Producer Extends, Consumer Super.
PECS stands for Producer Extends, Consumer Super. If a collection produces items (you read from it), use
? extends T. If it consumes items (you write to it), use ? super T. Example: void copy(List<? extends T> source, List<? super T> dest). Source produces items (extends), dest consumes them (super).Question 6
Medium
Does this compile?
class Pair<T> {
T first, second;
Pair(T a, T b) { first = a; second = b; }
}
Pair<Number> p = new Pair<>(1, 2.5);1 is Integer, 2.5 is Double. Both are Number subclasses.
Yes, it compiles. T is Number. Integer (1) and Double (2.5) are both subtypes of Number.
Question 7
Hard
What is the output?
import java.util.*;
public class Main {
static <T> List<T> merge(List<? extends T> a, List<? extends T> b) {
List<T> result = new ArrayList<>();
result.addAll(a);
result.addAll(b);
return result;
}
public static void main(String[] args) {
List<Integer> ints = List.of(1, 2);
List<Double> dbls = List.of(3.0, 4.0);
List<Number> merged = merge(ints, dbls);
System.out.println(merged);
}
}T is inferred as Number (common supertype of Integer and Double).
[1, 2, 3.0, 4.0]Question 8
Hard
Why can you not create generic arrays like
new T[10]?Think about the relationship between arrays and generics at runtime.
Arrays carry runtime type information (they are reified and covariant). Generics are erased at runtime. If
new T[10] were allowed, the array would be Object[] at runtime, violating the array's type guarantee. This could lead to ArrayStoreException being missed, creating type-unsafe situations.Question 9
Hard
What is the output?
import java.util.*;
public class Main {
static void addToList(List<? super Integer> list) {
list.add(1);
list.add(2);
}
public static void main(String[] args) {
List<Number> nums = new ArrayList<>();
nums.add(0.5); // Double
addToList(nums);
System.out.println(nums);
System.out.println(nums.get(0).getClass().getSimpleName());
System.out.println(nums.get(1).getClass().getSimpleName());
}
}List contains a Double and two Integers after addToList.
[0.5, 1, 2]DoubleIntegerQuestion 10
Medium
What is the difference between a bounded type parameter (
<T extends Number>) and an upper bounded wildcard (<? extends Number>)?One is used in class/method declarations, the other in method parameters.
Bounded type parameters (
<T extends Number>) are used in class or method declarations. They give you a named type T that you can reference throughout the class/method. Upper bounded wildcards (<? extends Number>) are used in method parameters when you do not need to reference the specific type. Wildcards provide more flexibility but less control.Multiple Choice Questions
MCQ 1
What does represent in a generic class?
Answer: B
B is correct.
B is correct.
T is a type parameter (placeholder) that gets replaced by an actual type (like String, Integer) when the generic class is instantiated.MCQ 2
Which of these is a valid generic declaration?
Answer: B
B is correct. Generic type parameters are declared in angle brackets:
B is correct. Generic type parameters are declared in angle brackets:
class Box<T>. Primitive types (int) cannot be used. Parentheses and braces are invalid syntax.MCQ 3
Why were generics introduced in Java?
Answer: B
B is correct. Generics provide compile-time type safety (catching type errors before runtime) and eliminate the need for explicit casting when retrieving elements from collections.
B is correct. Generics provide compile-time type safety (catching type errors before runtime) and eliminate the need for explicit casting when retrieving elements from collections.
MCQ 4
What happens to generic type information at runtime?
Answer: B
B is correct. Java uses type erasure: all generic type parameters are replaced by Object (or the bound) at compile time. No generic type information exists at runtime.
B is correct. Java uses type erasure: all generic type parameters are replaced by Object (or the bound) at compile time. No generic type information exists at runtime.
MCQ 5
What does
? extends Number mean in a method parameter?Answer: B
B is correct.
B is correct.
? extends Number is an upper bounded wildcard. It accepts Number or any subclass (Integer, Double, Long, etc.). You can read from such a collection but not write to it.MCQ 6
What does
? super Integer mean?Answer: C
C is correct.
C is correct.
? super Integer is a lower bounded wildcard. It accepts Integer or any superclass (Number, Object). You can write Integers to such a collection.MCQ 7
What does PECS stand for?
Answer: B
B is correct. PECS: Producer Extends, Consumer Super. Use
B is correct. PECS: Producer Extends, Consumer Super. Use
extends when reading from a collection (it produces items). Use super when writing to it (it consumes items).MCQ 8
Which of these is NOT allowed with generics?
Answer: C
C is correct. Generic array creation (
C is correct. Generic array creation (
new T[]) is not allowed because arrays carry runtime type information (reified) while generics are erased. This would create a type safety hole.MCQ 9
Is
List<String> a subtype of List<Object>?Answer: B
B is correct. Generics are invariant.
B is correct. Generics are invariant.
List<String> is NOT a List<Object>, even though String extends Object. If it were, you could add an Integer to a String list through the Object reference. Use List<? extends Object> for covariance.MCQ 10
What is the purpose of
<T extends Number & Comparable<T>>?Answer: B
B is correct. Multiple bounds use
B is correct. Multiple bounds use
& (not |). T must satisfy ALL bounds: extend Number AND implement Comparable. The class bound must come first, followed by interface bounds.MCQ 11
Can you overload methods that differ only in generic type parameters?
Answer: B
B is correct.
B is correct.
void process(List<String> list) and void process(List<Integer> list) cannot coexist. After type erasure, both become void process(List list), which is a duplicate method definition.MCQ 12
Which is a valid restriction of generics?
Answer: B
B is correct.
B is correct.
new T() is illegal because the JVM does not know what T is at runtime (type erasure). Multiple type parameters (Map<K,V>) and generic interfaces are fully supported.Coding Challenges
Challenge 1: Generic Stack
EasyImplement a generic Stack using an ArrayList internally. It should support push(T), pop(), peek(), isEmpty(), and size(). Test with Integer and String stacks.
Sample Input
Push 10, 20, 30. Pop twice.
Sample Output
Stack: [10, 20, 30]
Pop: 30
Pop: 20
Peek: 10
Size: 1
Use generics. Handle empty stack with IllegalStateException.
import java.util.ArrayList;
class Stack<T> {
private ArrayList<T> items = new ArrayList<>();
void push(T item) { items.add(item); }
T pop() {
if (isEmpty()) throw new IllegalStateException("Stack is empty");
return items.remove(items.size() - 1);
}
T peek() {
if (isEmpty()) throw new IllegalStateException("Stack is empty");
return items.get(items.size() - 1);
}
boolean isEmpty() { return items.isEmpty(); }
int size() { return items.size(); }
@Override
public String toString() { return items.toString(); }
}
public class Main {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>();
stack.push(10);
stack.push(20);
stack.push(30);
System.out.println("Stack: " + stack);
System.out.println("Pop: " + stack.pop());
System.out.println("Pop: " + stack.pop());
System.out.println("Peek: " + stack.peek());
System.out.println("Size: " + stack.size());
}
}Challenge 2: Generic Pair with Swap
EasyCreate a generic Pair class with methods getFirst(), getSecond(), and swap() that returns a new Pair with swapped types. Test with Pair.
Sample Input
Pair('Arjun', 20)
Sample Output
Original: (Arjun, 20)
Swapped: (20, Arjun)
swap() must return a new Pair with swapped type parameters.
class Pair<A, B> {
A first;
B second;
Pair(A a, B b) { first = a; second = b; }
A getFirst() { return first; }
B getSecond() { return second; }
Pair<B, A> swap() { return new Pair<>(second, first); }
@Override
public String toString() { return "(" + first + ", " + second + ")"; }
}
public class Main {
public static void main(String[] args) {
Pair<String, Integer> p = new Pair<>("Arjun", 20);
System.out.println("Original: " + p);
Pair<Integer, String> swapped = p.swap();
System.out.println("Swapped: " + swapped);
}
}Challenge 3: Generic Min/Max Finder
MediumWrite generic methods min() and max() that find the minimum and maximum elements in a List>. Also write a range() method that returns a Pair of (min, max). Test with Integer and String lists.
Sample Input
List: [30, 10, 50, 20, 40]
Sample Output
Min: 10, Max: 50
Range: (10, 50)
String min: Apple, max: Cherry
Use bounded type parameters with Comparable.
import java.util.List;
class Pair<A, B> {
A first; B second;
Pair(A a, B b) { first = a; second = b; }
@Override
public String toString() { return "(" + first + ", " + second + ")"; }
}
public class Main {
static <T extends Comparable<T>> T min(List<T> list) {
T result = list.get(0);
for (T item : list) {
if (item.compareTo(result) < 0) result = item;
}
return result;
}
static <T extends Comparable<T>> T max(List<T> list) {
T result = list.get(0);
for (T item : list) {
if (item.compareTo(result) > 0) result = item;
}
return result;
}
static <T extends Comparable<T>> Pair<T, T> range(List<T> list) {
return new Pair<>(min(list), max(list));
}
public static void main(String[] args) {
var nums = List.of(30, 10, 50, 20, 40);
System.out.println("Min: " + min(nums) + ", Max: " + max(nums));
System.out.println("Range: " + range(nums));
var strs = List.of("Banana", "Apple", "Cherry");
System.out.println("String min: " + min(strs) + ", max: " + max(strs));
}
}Challenge 4: Generic Filter Method
MediumWrite a generic method filter(List list, Predicate condition) that returns a new list containing only elements that satisfy the condition. Test with filtering even numbers and strings longer than 3 characters.
Sample Input
Numbers: [1,2,3,4,5,6], Strings: ['Hi','Hello','Hey','Howdy']
Sample Output
Even numbers: [2, 4, 6]
Long strings: [Hello, Howdy]
Use generics and Predicate functional interface.
import java.util.*;
import java.util.function.Predicate;
public class Main {
static <T> List<T> filter(List<T> list, Predicate<T> condition) {
List<T> result = new ArrayList<>();
for (T item : list) {
if (condition.test(item)) result.add(item);
}
return result;
}
public static void main(String[] args) {
List<Integer> nums = List.of(1, 2, 3, 4, 5, 6);
System.out.println("Even numbers: " + filter(nums, n -> n % 2 == 0));
List<String> strs = List.of("Hi", "Hello", "Hey", "Howdy");
System.out.println("Long strings: " + filter(strs, s -> s.length() > 3));
}
}Challenge 5: Generic Transform (Map) Method
HardWrite a generic method transform(List list, Function mapper) that applies a transformation function to each element and returns a List. Test with: numbers to their squares, strings to their lengths, students to their names.
Sample Input
Numbers: [1,2,3,4], Strings: ['Java','Go','Python']
Sample Output
Squares: [1, 4, 9, 16]
Lengths: [4, 2, 6]
Use two type parameters T and R. Use Function<T,R> interface.
import java.util.*;
import java.util.function.Function;
public class Main {
static <T, R> List<R> transform(List<T> list, Function<T, R> mapper) {
List<R> result = new ArrayList<>();
for (T item : list) {
result.add(mapper.apply(item));
}
return result;
}
public static void main(String[] args) {
List<Integer> nums = List.of(1, 2, 3, 4);
System.out.println("Squares: " + transform(nums, n -> n * n));
List<String> strs = List.of("Java", "Go", "Python");
System.out.println("Lengths: " + transform(strs, String::length));
System.out.println("Uppercase: " + transform(strs, String::toUpperCase));
}
}Need to Review the Concepts?
Go back to the detailed notes for this chapter.
Read Chapter NotesWant to learn Java with a live mentor?
Explore our Java Masterclass