[Java] Generic

HeyyJunn·2025년 3월 23일

[Java] Archive

목록 보기
4/4
post-thumbnail

💡 이 게시글은 김영한 강사님의 자바 강의를 수강하며 학습한 내용을 중요한 키워드 중심으로 정리한 개인 학습 기록입니다. 오직 기억 복기를 위한 목적으로 작성되었으며, 모든 내용을 포함하지 않으므로 학습 자료로는 적합하지 않습니다.

임시 저장본이 날아가는 경우가 있어 demo로 업로드

Generic1

제네릭의 핵심은 사용할 타입을 미리 결정하지 않는다는 점이다.
클래스 내부에서 사용하는 타입을 클래스를 정의하는 시점에 결정하는 것이 아니라 실제 사용하는 생성 시점에 타입을 결정하는 것이다.

public class GenericBox<T> {

    private T value;
    public void set(T value) {
        this.value = value;
    }
    public T get() {
        return value;
    }
}

타입 매개변수: GenericBox<T> 에서 T
타입 인자:
GenericBox<Integer> 에서 Integer
GenericBox<String> 에서 String

제네릭 클래스, 제네릭 인터페이스를 모두 합쳐서 제네릭 타입이라 한다.
GenericBox<T> 를 제네릭 타입이라 한다.

제네릭 명명 관례

주로 사용하는 키워드는 다음과 같다.
E - Element
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

타입 인자로 기본형은 사용할 수 없다
제네릭의 타입 인자로 기본형(int , double ..)은 사용할 수 없다. 대신에 래퍼 클래스(Integer , Double )를 사용
하면 된다.

GenericBox<Object> integerBox = new GenericBox<>();

Generic2

package generic.ex3;
import generic.animal.Animal;

public class AnimalHospitalV3<T extends Animal> {
    private T animal;

    public void set(T animal) {
        this.animal = animal;
    }

    public void checkup() {
        System.out.println(animal.getName());
        System.out.println(animal.getSize());
        animal.sound();
    }
    public T getBigger(T target) {
        return animal.getSize() > target.getSize() ? animal : target;
    }
}

여기서 핵심은 <T extends Animal> 이다.
타입 매개변수 TAnimal 과 그 자식만 받을 수 있도록 제한을 두는 것이다.
T 의 상한이 Animal 이 되는 것이다.

Generic Method

public static <T> T genericMethod(T t) { ... }

✔ 클래스 전체에 제네릭이 필요하지 않지만,
✔ 특정 메서드에서만 타입을 유연하게 하고 싶을 때 사용함

메서드 안에서 타입 매개변수 <T>를 직접 선언하고, 이 타입을 파라미터나 반환 타입으로 사용하는 메서드

제네릭 메서드는 클래스 전체가 아니라 특정 메서드 단위로 제네릭을 도입할 때 사용한다.

제네릭 메서드의 핵심은 메서드를 호출하는 시점에 타입 인자를 전달해서 타입을 지정하는 것이다. 따라서 타입을 지정하면서 메서드를 호출한다.

package generic.ex4;

public class GenericMethod {
    // 1. 그냥 Object로 받는 일반 메서드
    public static Object objMethod (Object obj) {
        System.out.println("object print: " + obj);
        return obj;
    }
    // 2. 제네릭 메서드 (타입 파라미터 T)
    public static <T> T genericMethod (T t) {
        System.out.println("generic print: " + t);
        return t;
    }
    // 3. 제네릭 + 타입 제한 (T는 Number 또는 그 하위 클래스만)
    public static <T extends Number> T numberMethod (T t) {
        System.out.println("bound print: " + t);
        return t;
    }
}

Generic Method - static, instance method

class Box<T> { //제네릭 타입
    static <V> V staticMethod2(V t) {} //static 메서드에 제네릭 메서드 도입
    <Z> Z instanceMethod2(Z z) {} //인스턴스 메서드에 제네릭 메서드 도입 가능
}

static 메서드는 클래스가 로드될 때 메모리에 올라오고,
이 시점엔 클래스의 제네릭 타입 T가 아직 결정되지 않았기 때문에,
메서드 안에서 <T> 를 직접 선언하면 클래스와 상관없이 메서드 호출 시점에 타입을 알아서 정할 수 있음.

<T>는 타입 매개변수 선언

`<T>`는 “타입을 나중에 정하자”는 뜻이고, 
클래스에서 쓰면 “객체 만들 때 정하고”, 
메서드에서 쓰면 “메서드 호출할 때 정한다."

그 뒤에 오는 T는 그 선언한 타입을 리턴 타입으로 쓰겠다는 뜻
매개변수의 (T t)는 그 타입의 값을 받아 쓰겠다는 뜻

Generic Method - Type Parameter Bound

public static <T extends Number> T numberMethod(T t) {}

Generic Type, Generic Method

static method는 클래스의 T를 알 수 없기 때문에 반드시 <T>로 제네릭 메서드 선언을 해야 하고,
ìnstance method는 이미 클래스의 제네릭 타입이 정해진 상태라서 클래스의 T든, 메서드의 T든 둘 다 사용 가능하다!

Generic Type Generic Method 의 우선순위

🌱

스프링을 빠르게 배우기 위해서 중급1편 강의의 일부를 skip 했더니 헷갈리는 부분이 생겼다.

찝찝하게 넘어가는걸 못해서 따로 공부해보기로 ~..

getClass().getName() 에서의 getName()animal.getName() 에서의 getName() 의 차이점을 모르겠다는 문제였다.

getClass()

public final Class<?> getClass()

이건 자바에서 모든 객체가 사용할 수 있는 메서드고, Object 클래스에 정의되어 있다.

반환값
Class<?>

즉, 자바의 “클래스를 표현하는” 객체를 반환한다.
이 객체는 자바 내부에서 “클래스 자체”를 설명하는 메타 정보를 담고 있다.

String s = "hello";

System.out.println(s.getClass());                  // class java.lang.String
System.out.println(s.getClass().getName());        // java.lang.String
System.out.println(s.getClass().getSimpleName());  // String
System.out.println(s.getClass().getSuperclass());  // class java.lang.Object

s.getClass()는 클래스 자체를 설명하는 객체고,
s.getClass().getName()은 그 클래스의 이름만 문자열로 꺼낸 것이다.

getClass().getName() 에서의 getName()
이 경우의 getName()은 진짜 내장 API 메서드.
getName() → Class 클래스의 메서드 (클래스 이름을 문자열로 알려줌).

animal.getName() 에서의 getName()
내가 Animal 클래스 안에서 직접 만든 일반 메서드

🌱 getName()만 쓰면 안 되고, 꼭 getClass().getName()처럼 써야 하나?

getName()은 클래스(Class 객체)에 정의된 메서드지, 만든 객체(예: String, Animal 등) 자체에는 getName()이라는 메서드가 반드시 있는 게 아님

그래서 getName()을 쓰려면 먼저 getClass()를 통해 “클래스를 설명하는 객체”를 가져와야 함.

s.getClass().getName()

"s 객체의 클래스가 뭐냐? → 그 클래스 객체에서 이름이 뭐냐?”

🌱 Animal 클래스 내부의 getName() 이 오버라이드가 아닌 이유

getClass().getName()의 getName()은 Class 클래스의 내장 메서드,
animal.getName()의 getName()은 네가 Animal 클래스에서 만든 일반 메서드 이다.

이름이 같을 뿐 완전히 다른 존재이기 때문에 ( 상속 관계도 없고, 다른 클래스에 있으므로 전혀 관련이 없음 ) 오버라이드 에 해당되지 않는다.

package generic.animal;

public class Animal {

    private String name;
    private int size;

    public Animal(String name, int size) {
        this.name = name;
        this.size = size;
    }
    public String getName() {
        return name;
    }

    public int getSize() {
        return size;
    }
    public void sound() {
        System.out.println("동물 울음 소리");
    }

    @Override
    public String toString() {
        return "Animal{" +
                "name='" + name + '\'' +
                ", size=" + size +
                '}';
    }
}
package generic.ex4;
import generic.animal.Animal;

public class ComplexBox<T extends Animal> {
    private T animal;

    public void set(T animal) {
        this.animal = animal;
    }

    public <T> T printAndReturn(T t) {
        System.out.println("animal.className: " + animal.getClass().getName());
        System.out.println("t.className: " + t.getClass().getName());
        // t.getName(); // 호출 불가 메서드는 <T> 타입이다. <T extends Animal> 타입이 아니다.
        return t;
    }
}

클래스의 <T>와 메서드의 <T>는 완전히 다른 T다.

제네릭 타입(T) 이 클래스에 이미 있어도,
메서드에서 <T>를 다시 선언하면 그 메서드 안에서는 그게 우선 적용된다.

그래서 메서드의 <T>는 클래스의 T와 완전 별개이며, 이름이 같아도 자기 영역에서는 메서드의 T가 우선이다.

profile
Data Science Major, School of AI · Convergence Software - Myongji University (SEOUL)

0개의 댓글