How do you declare and use a generic class with multiple type parameters, and how does it differ from a generic interface like Map<K,V>?
answer
- Comma-separate parameters: class Pair<K, V>
- Each parameter is independent; Pair<A,B> != Pair<B,A>
- Map<K,V> is the canonical multi-param generic interface
- Implementing class: fix the args, or stay generic and forward them
- Position, not name, binds class params to interface params
basics
~20 sList several type parameters in the angle brackets separated by commas, like class Pair<K, V>. Each one is an independent placeholder you can use for fields, parameters, and return types. A generic interface such as Map<K,V> is the same idea but for an interface — implementing classes supply or pass along those types.
solid answer
~40 sYou declare multiple type parameters by separating them with commas inside the angle brackets: class Pair<K, V>. K and V are two independent placeholders; you can use each freely for fields (K key, V value), constructor and method parameters, and return types (K getKey(), V getValue()). When you instantiate it you supply both arguments: new Pair<String, Integer>(...). A generic interface like Map<K,V> declares the same kind of multi-parameter contract but as an interface — methods like V get(K key) and V put(K key, V value) tie the parameters together. An implementing class either fixes the parameters (class StringIntMap implements Map<String,Integer>) or stays generic and threads them through (class MyMap<K,V> implements Map<K,V>). The conventional names are K/V for key/value, but they are just names and could be anything.
go deeper
Knows you separate type parameters with commas (Pair<K,V>) and can instantiate it; recognizes Map<K,V> as a generic interface.
Can write a multi-parameter class, distinguish it from a generic interface, and show both ways an implementing class handles the interface's parameters (fix vs forward).
Explains parameter independence, position-based binding to an interface, interaction with erasure (no overload-by-argument), and dependent bounds between parameters.
Reasons about API ergonomics of multi-parameter generics (parameter ordering, when two parameters are better than a nested type), and consistency with JDK conventions across an evolving library.
## One parameter is not always enough Many abstractions naturally involve more than one type. A key-value pair, a map, a function from input to output, or a result-plus-error all need two (or more) independent type slots. Java lets a generic class or interface declare **several type parameters**. ## Declaring multiple type parameters List them comma-separated inside the angle brackets: ```java public class Pair<K, V> { private final K key; private final V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } ``` Key points: - `K` and `V` are **independent**. `Pair<String, Integer>` and `Pair<Integer, String>` are different parameterized types. - Each may be used anywhere a type is expected inside the class. - Conventional names: `K`=key, `V`=value, `T`/`U`/`R`=arbitrary types, `E`=element, `N`=number. They are only conventions; the compiler treats them as ordinary identifiers. ## Instantiating ```java Pair<String, Integer> age = new Pair<>("age", 30); String k = age.getKey(); // String, no cast Integer v = age.getValue(); // Integer, no cast ``` The diamond `<>` infers both arguments from the left-hand declaration. ## Generic interfaces with multiple parameters A generic **interface** is declared identically, but it defines a *contract* rather than an implementation. The JDK's `Map<K, V>` is the classic example: ```java public interface Map<K, V> { V get(Object key); V put(K key, V value); // ... } ``` Notice how the parameters relate the methods: `put` takes a `K` and a `V`, and `get` returns a `V`. That relationship is the whole point — it guarantees that what you put in under a key comes back out as the same value type. ## How a class uses a generic interface — two choices **1. Fix the parameters** (the class is *not* generic): ```java public class WordCount implements Map<String, Integer> { // here K is permanently String and V permanently Integer } ``` **2. Stay generic and pass the parameters through:** ```java public class MyHashMap<K, V> implements Map<K, V> { // this class's own K,V are forwarded to the interface's K,V } ``` In the second case the class declares its *own* type parameters and supplies them as the interface's arguments. The names matching (`K`,`V`) is convention; what matters is the *position* — the first class parameter becomes the interface's first parameter. ## Difference between a generic class and a generic interface Mechanically they declare type parameters the same way. The differences are the ordinary class-vs-interface ones: - An interface only declares the contract (method signatures, possibly `default` bodies); a class can hold generic *state* (`private K key`). - A class can have at most one superclass but implement many generic interfaces, e.g. `class X<T> implements Iterable<T>, Comparable<X<T>>`. - You instantiate a class (`new Pair<>(...)`); you cannot instantiate an interface, only implement it. ## Erasure still applies At runtime, `Pair<String,Integer>` and `Pair<Integer,String>` are both just `Pair`; `K` and `V` erase to `Object` (their default bound). So you cannot overload two methods that differ only by `Pair<String>` vs `Pair<Integer>` — after erasure they have identical signatures. ## Why it matters Multi-parameter generics let you write one type-safe abstraction (a map, a pair, a function) reused across every combination of element types, with the compiler verifying each use and removing casts. The entire Collections Framework (`Map<K,V>`, `Entry<K,V>`) and the functional interfaces (`Function<T,R>`, `BiFunction<T,U,R>`) are built this way.
- When MyHashMap<K,V> implements Map<K,V>, what links the class's K to the interface's K?Position. The class declares its own parameters MyHashMap<K,V> and then supplies them as the interface's arguments in implements Map<K,V>. The first class parameter is bound to the interface's first parameter regardless of the name; matching names is just readability.
- Can the second type parameter's bound mention the first, like <T, U extends List<T>>?Yes. A later type parameter may reference an earlier one in its bound, e.g. <T, U extends Comparable<T>>. This creates a dependency between the parameters that the compiler enforces at every use site.
saying these in an interview costs you the question
- Thinking the parameter names must literally be K and V — they are conventions, any identifier works
- Believing Pair<String,Integer> and Pair<Integer,String> are interchangeable
- Assuming a generic interface can hold fields — interfaces declare contracts, not generic state
- Trying to overload methods that differ only by type arguments (identical after erasure)