[JAVA] 4주차 : 제네릭

INHEES·2023년 8월 16일

Java

목록 보기
8/13

JAVA 4주차 문법 : 제네릭

타입의 중요성

javascript, python 과 같은 약타입 언어를 이용한다면 기본적으로 변수의 타입 지정없이 함수를 구현 가능하다.

  public class Generic {
      public Object plusReturnFunction(Object a,Object b) { ... }
  }

java 안에서 객체는 Object Class 를 상속한다. 때문에 위와 같은 Object type 으로 변수를 정의한다면 파라미터를 전달하는데 문제가 되지는 않아 보인다.
하지만 이런 경우 타입 안정성이 침해 받게 된다. 두개 이상의 파라미터가의 타입이 다를 때 순서 로직에서 더 많은 코드를 작성하는 문제가 생기는 것이다.

제네릭이란?

  • 제네릭 이란 데이터형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법 이다.
  • 클래스, 메서드에 사용 가능하며 <> 안에 들어갈 타입변수의 이름을 지정한다. 일종의 컨벤션으로 T로 지정한다.
  • 제네릭을 통해 클래스가 생성되었다면, 인스턴스를 만들기 위해 타입변수에 들어갈 실제 값을 넣어주어야 한다. ex) String
public class Generic<T> {
    private T t;
    public T get() {
        return this.t;
    }
    public void set(T t) {
        this.t = t;
    }

    public static void main(String[] args) {
        Generic<String> stringGeneric = new Generic<>();
        stringGeneric.set("Hello World");
        String tValueTurnOutWithString = stringGeneric.get();
        System.out.println(tValueTurnOutWithString);
    }
}

제네릭 용어

  // Generic Class
  public class Generic<T> { ... }
  
  // T = 타입 변수 
  Generic<String> stringGeneric = new Generic<>();

Generic Class 를 원시 타입 이라고 부르는데 객체가 인스턴스화 되지 않는것을 의미한다.

Genericstatic 멤버에 사용이 불가하다.
타입 변수는 인스턴스 변수로 간주 되기 때문이며, 배열 또한 생성할 수 없다.

제네릭 문법

1. 다수의 타입변수를 사용 가능하다.

  public class Generic<T, U, E> {
      public E multiTypeMethod(T t, U u) { ... }
  }


  Generic<Long, Integer, String> instance = new Generic();
  instance.multiTypeMethod(longVal, intVal);

2. 다형성, 상속과 타입의 관계는 그대로 작용한다.

  • 부모 클래스로 제네릭 타입변수를 지정하고 자식 클래스가 넘겨 받는다.

3. 와일드 카드 를 통해 제네릭의 제한을 정할 수 있다.

  public class ParkingLot<T extends Car> { ... }

  ParkingLot<BMW> bmwParkingLot = new ParkingLot();
  ParkingLot<Iphone> iphoneParkingLot = new ParkingLog(); // error
Error 의 해결 방한으로 다음과 같다. 
1. <? extends T> : T와 그 자손들만 사용 가능
2. <? super T> : T와 그 조상들만 가능
3. <?> : 제한 없음  

4. 메서드를 스코프로 제네릭을 별도로 선언 가능하다.

  static <T> void sort(List<T> list, Comparator<? super T> c){...}
  • 위의 코드에서 반환 타입앞에 <> 제네릭을 사용한 경우, 해당 메서드만 적용되는 타입변수를 선언 가능하다.
  • 제네릭 메서드의 타입변수는 해당 메서드에만 적용되기에 static 메서드에 사용 가능한 것이다.
  public class Generic<T, U, E> {
  // Generic<T,U,E> 의 T와 아래의 T는 이름만 같을뿐 다른 변수
      static <T> void sort(List<T> list, Comparator<? super T> c) { ... }
  }
  • 같은 이름의 변수 T라도 메서드와 클래스의 타입변수는 서로 다른 변수이다.

제네릭과 Collection

Java 의 Collection 은 interface 로 구성되어져 있다.

  // 실제 java.util의 List 코드
  public interface List<E> extends Collection<E> {
          int size();
      boolean isEmpty();
          ...
          boolean addAll(Collection<? extends E> c);
          boolean add(E e);
          ...
  }
  • List 인터페이스는 제네릭 인터페이스 이다.
  • addAll() 함수를 뜯어보면
    • Collection 타입에 속한다.
    • List 타입변수 E의 자손 클래스를 원소로 가지고 있다.

intellij 에서 ctrl + n을 누르고 List 를 입력하면 java.util 안의 구성된 내용을 볼 수 있다.


Wrapper 객체

Wrapper 객체는 원시타입을 추상화 시키는 역할을 한다.

그림에서 보이듯이 원시타입의 변수의 경우 이상의 의미를 가지는경우 메모리 사용량과 같은 성능상의 이유로 기본값을 객체화 하는 Wrapper Class 를 이용한다.

  Integer num = new Integer(17);  // Boxing
  int n = num.intValue(); // UnBoxing

  // JDK 15 부터는 AutoBoxing 기능이 있다.
  Character ch = 'X'; // AutoBoxing
  char c = ch; // AutoUnBoxing

때문에 박싱해서 객체화된 원시값들은 클래스처럼, 구현되어 있는 메서드를 사용 가능한 것이다. 즉 객체만 할 수 있는 기능을 사용가능한 것이다.


정리

이번시간에는 제네릭에 대한 활용법과 java.util 에 들어있는 Collection 기능들을 이해할 수 있었다. 다음시간에는 Thread 에 대해 알아본다.

profile
이유를 찾아보자

0개의 댓글