Generics
Generics allow you to parameterize types (classes, interfaces, methods).
- Instead of specifying a type upfront, you defer that decision to when the class or method is used (compile-time, not runtime).
- Used extensively in collections (e.g.,
List<E>
,Map<K,V>
). - Cannot be used with primitive types directly → Use wrapper classes (
int → Integer
,double → Double
) - Cannot use with class-level variables
- generics are type parameters bound to instances, and static members belong to the class itself, not any particular instance
- If you could use generics to class variables, then the type of the class variables would differ by instance, making them not share the same variable
- Therefore, there’s no type context for the static field/method
Common Type Parameter Names (convention):
- Although you can use lowercase names or other identifiers, ☕Java convention prefers these uppercase letters for clarity.
- These are just names, and the actual types are determined during runtime (when they are actually used)
Symbol | Meaning |
---|---|
T | Type |
E | Element |
K | Key |
V | Value |
N | Number |
Generic Class
- Classes where generics are being applied
<T>
is the type parameter
class Basket<T> {
private T item;
public Basket(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
- Since generic classes are not tied to a specific type, when instantiating a generic class, you need to specify the intended type like below:
Basket<String> basket1 = new Basket<String>("Hello");
Basket<Integer> basket2 = new Basket<Integer>(10);
Basket<Double> basket3 = new Basket<Double>(3.14);
Basket<String> basket1 = new Basket<>("Hello"); // ✅ allowed
- you cannot use primitive types (like
int
,double
, etc.) as type arguments → use wrapper classes - The compiler can infer the type from the variable declaration.
Generic method
Generic
While you can declare an entire class as generic, you can also declare only specific methods inside the class as generic. These are called generic methods.
- A generic method defines its type parameter before the return type, and this type parameter is only valid within that method.
- 내부 구조 (static, heap, class method ) 이해 + 복습!
class Basket<T> {
private T item;
public Basket(T item) {
this.item = item;
}
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
....
}
- The type parameter of a generic method is completely separate from the class’s generic type parameter — even if they use the same letter.
class Basket<T> {
public <T> void add(T element) {
// This T is NOT the same as the class's T
}
}
- Class type parameter is decided at instantiation time
- Method type parameter is decided at method call time
Basket<String> basket = new Basket<>();
basket.<Integer>add(10); // 메서드 내 T = Integer
basket.add(10); // 타입 추론 가능
basket.<Integer>add(10);
- the generic method
public <T> void add(T element)
is called withT = Integer
- “For this call to
add
, treat the type parameter<T>
asInteger
.” - If the method was just
public void add(T element) { ... }
, we wouldn’t be able to override the type
- the generic method
Printing stuff in list
public class GenericPrinter {
public static <T> void printArray(T[] array) {
for (T elem: array) {
System.out.println(element);
}
}
}
String[] names = {"Alice", "Bob", "Charlie"};
Integer[] numbers = {1, 2, 3};
GenericPrinter.printArray(names);
GenericPrinter.printArray(numbers);
Comparable
public class GenericComparator {
public static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
}
System.out.println(GenericComparator.max(10, 20));
System.out.println(GenericComparator.max("apple", "banana"));
public static <T extends Comparable<T>> T max(T a, T b)
<T extends Comparable<T>>
: type parameterT
is a type that must implementComparable<T>
- Only types that can compare themselves to other instances of the same type (
T
) can be used here.Integer implements Comparable<Integer>
Double implements Comparable<Double>
T
should be something that can use the.compareTo()
method
T
: return type- same
T
declared in the type parameter
- same
max(T a, T b)
- Parameters: two values of type
T
- Parameters: two values of type
Polymorphism
class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }
class Basket<T> {
private T item;
public T getItem() {
return item;
}
public void setItem(T item) {
this.item = item;
}
}
class Main {
public static void main(String[] args) {
Basket<Flower> flowerBasket = new Basket<>();
flowerBasket.setItem(new Rose()); // 다형성 적용
flowerBasket.setItem(new RosePasta()); // 에러
}
}
Bounded Type Parameter
- This helps ensure that only objects with a specific interface or superclass can be used, allowing access to the methods defined in the bound
class Flower { ... }
class Rose extends Flower { ... }
class RosePasta { ... }
// HERE!!!
class Basket<T extends Flower> {
private T item;
public T getItem() { return item; }
public void setItem(T item) { this.item = item; }
}
class Main {
public static void main(String[] args) {
Basket<Rose> roseBasket = new Basket<>();
// Basket<RosePasta> rosePastaBasket = new Basket<>(); // 에러
}
}
- Now
Basket
only allows subclasses ofFlower
- You use
extends
keyword on both classes and interfacesextends
is used to set upper bounds on type parameters.super
is not used in type parameter declarations like<T super X>
— it’s only used in wildcard types (e.g.,List<? super Integer>
).
interface Plant { ... }
class Flower implements Plant { ... }
class Rose extends Flower implements Plant { ... }
class Basket<T extends Flower & Plant> {
private T item;
public T getItem() { return item; }
public void setItem(T item) { this.item = item; }
}
- You use the
&
operator for both classes and/or interfaces- Classes must come first, the rest interfaces (otherwise it’s a compile error)
Wildcard
- wildcards in generic can turn into ANYTHING
?
class Phone {
public <? extends Phone> void call(<? extends Phone>);
}
class IPhone extends Phone {}
class Galaxy extends Phone {}
class IPhone12Pro extends IPhone {}
class IPhoneXS extends IPhone {}
class S22 extends Galaxy {}
class ZFlip3 extends Galaxy {}
class Phone {
public void call(<? extends Phone> phone) {};
}
class IPhone extends Phone {}
class Galaxy extends Phone {}
class IPhone12Pro extends IPhone {}
class IPhoneXS extends IPhone {}
class S22 extends Galaxy {}
class ZFlip3 extends Galaxy {}
number ㄴ Integer ㄴ Double
-
printBox
- setValue를 쓸때 int인지 double인지 모름
-
addToBox
- getValue할때 super을 사용했으니까 object밖에 못들어옴
- object로 값을 받는건 제대로 조회하는게 아님
-
조회할때는 extends, 추가할때는 super