[JAVA] 제네릭 메서드의 오해를 풀어보자!❤‍🔥

Mando·2023년 3월 31일
1

JAVA

목록 보기
3/10

제네릭 메서드를 공부하다가

클래스의 <T> 와 제네릭 메서드의 <T>는 다르다

라는 이야기를 블로그에 보게 되었다.

Object o = Fruit.<Integer>staticAndGenericMethodWithDif(1);
    public static <S> T staticAndGenericMethodWithDif(T id) {
        return id;
    }

나는 클래스의 타입변수<T>와 제네릭 메서드의 타입변수 <S>가 다른 상황에서
클래스 타입변수의 타입을 반환하려면 어떻게 해야할까...? 라는 고민을 하게 되었다.
왜냐하면 해당 메서드를 호출할 때는 제네릭 메서드의 매개변수 타입을 사용하기 때문이다.

(지금 생각해보면 정말로 멍청한 생각이다...ㅋ 역시 코드를 직접 쳐보고 확인해보는 게 제일 좋은 방법이라는 걸 또 다시 깨닫게 되었다.)

그래서! 제네릭 메서드에 대한 오해를 풀고자! 예시 코드를 작성해보았다.

public class GenericTest {
    public static void main(String[] args) {
        Fruit<String> stringFruit = new Fruit<>();

        String s = stringFruit.notStaticMethod("오직 String만 입력할 수 있습니다.");
        System.out.println(s);
        System.out.println(s.getClass().getName());

        System.out.println("---------------------------------");

        String s1 = stringFruit.notStaticAndGenericMethodWithSame("object를 입력합니다. - string 입력");
        System.out.println(s1);
        System.out.println(s1.getClass().getName());

        System.out.println("---------------------------------");

        Integer integer = stringFruit.notStaticAndGenericMethodWithSame(1);
        System.out.println(integer);
        System.out.println(integer.getClass().getName());

        System.out.println("---------------------------------");

        String s2 = stringFruit.notStaticAndGenericMethodWithDif("오직 String만 입력할 수 있습니다");
        System.out.println(s2);
        System.out.println(s2.getClass().getName());

        System.out.println("---------------------------------");

        Integer integer1 = Fruit.<Integer>staticAndGenericMethodWithSame(1);
        System.out.println(integer1);
        System.out.println(integer1.getClass().getName());
    }
}
public class Fruit<T> {

    // 1번
    public T notStaticMethod(T id) {
        return id;
    }
  
 	// 2번
  	// 호출 시에 T의 타입을 정할 수가 없다.
  	// 따라서 컴파일 에러가 발생한다.
    public static T staticNotGenericMethod(T id){
        return id;
    }

  	//3번
    public <T> T notStaticAndGenericMethodWithSame(T id) {
        return id;
    }

  	//4번
    public <S> T notStaticAndGenericMethodWithDif(T id) {
        return id;
    }
  
  	//5번
//    public static <S> T staticAndGenericMethodWithDif(T id) {
//        return id;
//    }
  
	//6번
  	//호출 시에 S의 타입을 정할 수가 있다.
    public static <S> S staticAndGenericMethodWithSame(S id) {
        return id;
    }
}

실행결과는 아래와 같습니다.

오직 String만 입력할 수 있습니다.
java.lang.String
---------------------------------
object를 입력합니다. - string 입력
java.lang.String
---------------------------------
1
java.lang.Integer
---------------------------------
오직 String만 입력할 수 있습니다
java.lang.String
---------------------------------
1
java.lang.Integer

1번 : static ❌ + generic method ❌

```java
public class Fruit<T> {

    // 1번
    public T notStaticMethod(T id) {
        return id;
    }

이때는 클래스 타입변수 <T> 타입이 method의 return type, 매개변수의 type이다.

2번 : static ⭕️ + generic method ❌

public class Fruit<T> {
  	
  	// 2번
  	// 호출 시에 T의 타입을 정할 수가 없다.
  	// 따라서 컴파일 에러가 발생한다.
    public static T staticNotGenericMethod(T id){
        return id;
    }
}

static이면 인스턴스가 생성되지 않은 상황이다. 따라서 클래스 타입변수의 타입을 알 수 없다.
또한 해당 메서드를 호출할 때 T의 타입을 결정할 수도 없기 때문에
우리는 T의 타입을 알 수가 없다.

3번 : static ❌ + generic method ⭕️

public class Fruit<T> {
  	//3번
    public <T> T notStaticAndGenericMethodWithSame(T id) {
        return id;
    }
}

처음 해당 코드를 작성하면서 들었던 의문점

  • static 메서드에 제네릭을 사용하면 컴파일 에러가 나기 때문에 (2번의 경우) static 메서드에 제네릭을 사용하기 위해서 제네릭 메서드를 사용한다고 했다.
  • 근데 위 경우는 static 메서드가 아니기 때문에 제네릭을 사용할 수가 있다.(1번 경우)

그럼 3번의 경우도 static이 아니기 때문에 제네릭이 사용가능한데, 굳이 제네릭 메서드를 사용한 이유가 무엇일까?

놓치고 있었던 개념

그렇다!!! 이 포스팅의 처음에 언급했던 그 부분을 내가 놓치고 있었다!

클래스의 <T> 와 제네릭 메서드의 <T>는 다르다

3번 메서드가 필요한 이유?

  • 클래스의 타입 변수<T> 와 제네릭 메서드의 타입변수<T>는 기호만 같을 뿐 다른 type이 들어갈 수 있다!!!
  • 따라서 제네릭 메서드의 타입변수는 정해지지 않은 것이다.
  • 그렇기에 제네릭 메서드의 타입변수 <T>에는 Object가 들어갈 수 있다.

4번 : static ❌ + generic method ⭕️ (클래스의 제네릭 타입 =! 메서드의 제네릭 타입)

public class Fruit<T> {

  	//4번
    public <S> T notStaticAndGenericMethodWithDif(T id) {
        return id;
    }
}

⭐클래스의 <T> 와 제네릭 메서드의 <T>는 다르다⭐라고 했다‼︎

처음 해당 코드를 작성하면서 들었던 의문점

  • 제네릭 타입은 <S>이다. 하지만 메서드의 반환 타입은 T이다. 결국 제네릭인 T는 S와 동일한 거라고 봐야하는 건가?

놓치고 있었던 개념

클래스의 <T> 와 제네릭 메서드의 <T>는 다르다라고 했지
같으면 안 된다!!!!는 아니였다

4번 메서드가 필요한 이유?

클래스의 <T> 와 제네릭 메서드의 <T>는 다르다라는 것을 직관적으로 보여주고 있다.(클래스의 제네릭 타입과 메서드의 제네릭 타입이 다른 것을 눈으로 확인할 수 있다.)
하지만, 꼭 같아야 하는 것은 아니다.

클래스의 제네릭 타입 <T> 와 제네릭 메서드의 제네릭 타입<T>는 같을 수도 다를 수도 있다.

5번 : static ⭕️ + generic method ⭕️ (클래스의 제네릭 타입 ! = 메서드의 제네릭 타입) -> 컴파일 에러

public class Fruit<T> {
  	//5번
//    public static <S> T staticAndGenericMethodWithDif(T id) {
//        return id;
//    }

처음 해당 코드를 작성하면서 들었던 의문점

  • 제네릭 타입은 <S>이다. 하지만 클래스의 타입 변수는 <T>이다.
  • 그럼 해당 static 메서드를 사용할 때 T에 특정 타입의 값을 보내면 그 특정 타입의 값을 반환하는 게 아닌가?

놓치고 있었던 개념

  • 제네릭 클래스의 타입 변수 <T>는 인스턴스 변수이다. 인스턴스가 생성될 때마다 지정되는 것이기 때문이다.
  • 따라서 static 메서드에서 인스턴스 변수로 여겨지는 클래스의 타입 파라미터를 사용하고 있으므로 컴파일 에러가 발생한다.

5번 메서드가 필요하지 않은 이유?

위에 언급한 것처럼 클래스의 제네릭 타입 `<T>를 모르기 때문에 컴파일 에러가 발생한다.

아래 예시도 마찬가지로 **인스턴스가 생성되지 않았기에 <T> 타입을 정확하게 알지 못한다.

    public static <S> T staticAndGenericMethodWithDif(S id) {
        return id;
    }

6번 : static ⭕️ + generic method ⭕️ (클래스의 제네릭 타입 != 메서드의 제네릭 타입)

public class Fruit<T> {
	//6번
  	//호출 시에 S의 타입을 정할 수가 있다.
    public static <S> S staticAndGenericMethodWithSame(S id) {
        return id;
    }
}

6번이 알려주는 바

  • 클래스의 제네릭 타입 와 제네릭 메서드의 제네릭 타입는 같을 수도 다를 수도 있다.
    에서 다른 경우를 보여주고 있다.
  • static 제네릭 메서드를 호출하면서 전달해주는 값이 어떤 type이냐에 따라서 반환되는 값이 달라진다.
        Integer integer2 = Fruit.<Integer>staticAndGenericMethodWithSame(1);
        System.out.println(integer2);
        System.out.println(integer2.getClass().getName());

        System.out.println("---------------------------------");

        String string = Fruit.<String>staticAndGenericMethodWithSame("String");
        System.out.println(string);
        System.out.println(string.getClass().getName());
1
java.lang.Integer
---------------------------------
String
java.lang.String

➡️ 즉, 제네릭 메소드의 generic type은 지역 변수이다.

0개의 댓글