본문 바로가기

개발(합니다)/Java&Spring

[java-기초-13] 제네릭

반응형

왜 제네릭을 사용하는가?

java 5부터 제네릭 타입이 새로 추가되었고 제네릭 타입을 이용해 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있다.
제네릭은 널리 사용된다.

  • 컬렉션, 람다식, 스트림, NIC
  • java API docs
  • 클래스, 인터페이스, 메서드를 정의할 때 타입을 파라미터로 사용

제네릭의 특징

  1. 컴파일 시 강한 타입 체크를 할 수 있다.
  2. 타입 변환을 제거 한다.
        List list = new ArrayList<>();
        list.add("hello");
        String str = (String) list.get(0);

        List<String> list2 = new ArrayList<>();
        list.add("hello");
        String str2 = list2.get(0);

제네릭 타입 : class, Interface

Object를 대신 해 타입을 확정 짓는 용도로 사용한다.

public class Box {
    private Object object; // 자바의 모든 타입을 저장하고 싶어서 만듬
    public void set(Object object) {
        this.object = object;
    }
    public Object get() {
        return object;
    }
}

public class Apple {
}

  public static void main(String[] args) {
      Box box = new Box();
      box.set("Test");
      String name = (String) box.get();

      box.set(new Apple());
      Apple apple = (Apple) box.get();
  }
  • 모든 타입을 담을 수 있는 Box를 만들었다
  • 타입이 분명하지 않아 사용할 때면 형변환을 통해 사용해야 한다.

    제네릭 타입 사용 방법

  • public class Box<T> { private T object; public void set(T object) { this.object = object; } public T get() { return object; } } public static void main(String[] args) { Box<String> box2 = new Box<>(); box.set("Test"); String name2 = box2.get(); Box<Apple> box3 = new Box<>(); box.set(new Apple()); Apple apple2 = box3.get(); }
  • 지정한 타입으로 인식하여 형변환을 하지 않아도 된다.

멀티 타입 파라미터 : class<K, V, ...>, interface<K, V, ...>

제네릭 타입은 두대 이상의 멀티 타입 파라미터를 사용 할 수 있고 콤마로 구분한다.

  public class Product <K, V>{
    private K kind;
    private V model;

    public K getKind() { return this.kind; }
    public V getModel() { return this.model; }

    public void setKind(K kind) { this.kind = kind; }

    public void setModel(V model) { this.model = model; }
}

    public static void main(String[] args) {
        Product<String, String> p = new Product<>();
        p.setKind("1");
        p.setModel("M");

    }

제네릭 메서드 : <T, R> R method(T t)

제네릭 메서드는 매개 타입과 리턴 타입으로 타입 파라미터를 갖는 메서드를 말하며 리턴 타입 앞에 < > 기호를 추가하고 타입 파라미터를 기술한 다음 리턴 타입과 매개 타입으로 타입 파라미터를 사용하면 된다.

  public <타입 파라미터, ...> 리턴 타입 메서드명(매개변수, ...) { } // 제네릭 메서드 선언 방법

  리턴 타입 변수 = <구체적타입> 메서드명(매개값); // 명시적으로 구체적 타입을 지정
  리턴 타입 변수 = 메서드명(매개값); // 매개값을 보고 구체적 타입을 추정

제한된 타입 파라미터 : <T extends 최상위타입>, <T super 최하위타입>

타입 파라미터에 지정되는 구체적인 타입을 제한해야 할 경우가 종종 있다.
예를 들어 숫자를 연산하는 제네릭 메서드는 매개값으로 Number타입 또는 하위 클래스 타입(Byte, Short, Integer, Long, Double)의 인스턴스만 가져와야 할 때 제한된 타입 파라미터가 필요하며 이경우에는 <T extends 최상위타입>를 사용하고 반대는 <T super 최하위타입>를 사용한다.

<T extends 최상위타입> : 자기 자신과 하위 클래스들로 제한
<T super 최하위타입> : 자기 자신과 상위 클래스들로 제한

public class BoundedTypeParameter {
    public static void main(String[] args) {
//        String str = Util.compare("a", "b");

        int result1 = Util.compare(10, 20);
        System.out.println(result1);

        int result2 = Util.compare(3.5, 3);
        System.out.println(result2);

    }

    private static class Util {
        public static <T extends Number> int compare(T i, T i1) {
            return Double.compare(i.doubleValue(), i1.doubleValue());
        }
    }
}

와일드카드 타입 : <?>, <? extends ...>, <? super ...>

코드에서 ?를 일반적으로 와일드 카드라고 부르며 세 가지 형태로 사용할 수 있다.

  1. 제네릭 타입<?> : 제한 없음 - 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다.
  2. 제네릭 타입<? extneds 상위타입> : 상위 클래스 제한 - 타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 하위 타입만 올 수 있다.
  3. 제네릭 타입<? super 하위타입> : 하위 클래스 제한 - 타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위 타입만 올 수 있다.

객체가 위 그림처럼 되어 있다면

  • 제네릭 타입<?> : 수강생은 모든 타입(Person, Worker, Student, HighStudent)이 될 수 있다.
  • 제네릭 타입<? extneds Student> : 수강생은 Studnet와 HightStudent만 될 수 있다.
  • 제네릭 타입<? super Worker> : 수강생은 Worker와 Person만 될 수 있다.
public class Course <T>{
    private String name;
    private T[] students;

    public Course(String name, int capacity) {
        this.name = name;
        this.students = (T[]) (new Object[capacity]);
    }

    public String getName() { return this.name; }
    public T[] getStudents() { return this.students; }
    public void add(T t) {
        for (int i = 0; i < students.length; i++ ) {
            if (students[i] == null ) {
                students[i] = t;
                break;
            }
        }
    }
}

제네릭 타입의 상속과 구현

제네릭 타입도 다른 타입과 마찬가지로 부모 클래스가 될 수 있다.

    public class ChildProduct<T, M> extends Prodct<T, M> { ... } // 상속만 받아 구현한 형태
    public class ChildProduct<T, M, C> extends Prodct<T, M> { ... } // 상속만 받아 파라미터를 자식에서 추가한 형태
반응형