모르지 않다는 것은 아는것과 다르다.

Java

제네릭

채마스 2022. 2. 26. 00:46

제네릭 자료형

  • 제네릭 자료형이란? -> 클래스에서 사용하는 자료형이 여러개 일수 있고, 그 기능은 동일한 경우 클래스의 자료형을 특정하지 않고 일반화한 다음 추후 해당 클래스를 사용할 때 지정 할 수 있도록 선언
  • java 5 부터 지원되었다.
  • 실제 사용되는 자료형의 변환은 컴파일러에 의해 검증되므로 안정적인 프로그램 방식이다.
  • 컬렉션 프레임워크에서 많이 사용된다.
  • 예시는 아래와 같다.
public class GenericPrinter<T> {
    private T member;

    public void setMember (T member){
        this.member = member;
    }
    public T getMember(){
        return member;
    }
}
public class test {
    public static void main(String[] args){

        Member member = new Member();
        GenericPrinter<Member> memberPrinter = new GenericPrinter<>(); //타입 지정
        memberPrinter.setMember(member);

        Member member = memberPrinter.getMember();

    }
}
  • 자료형 매개변수 T(type parameter): 이 클래스를 사용하는 시점에 사용할 자료형을 지정한다.
  • static 변수는 사용할 수 없다.
  • GenericPrinter: 제네릭 자료형
  • E : element, K: value
  • T, E, K 외에 아무 문자로 지정할 수 있지만 의미있게 지정하는게 좋다.

<T extends 클래스> 사용하기

  • T 자료형의 범위를 제한 할 수 있다.
  • 클래스를 상속받는 자료형만 T 자리에 들어올 수 있다. 즉, T 자리엔 해당 클래스이거나, 자식들이 들어갈 수 있다.
  • 상위 클래스에서 선언하거나 정의하는 메서드를 활용할 수 있다.
  • 상속을 받지 않는 경우 T는 Object클래스가 기본으로 제공하는 메서드만 사용가능

<T extends 클래스 > -> 해당 클래스에서 상속받은 클래스만 T 자리에 올 수 있다.
<T extends Member> -> Member를 상속 받은 Person1, Person2만 T 자리에 올 수 있다.

T extends를 사용한 프로그래밍

  • GenericPrinter에 member 변수의 자료형을 상속받아 구현.
  • T에 무작위 클래스가 들어갈 수 없게 member변수를 상속받은 클래스로 한정.
  • 예시는 아래와 같다.
public class GenericPrinter<T extends Member> {
    private T member;

    public void setMember (T member){
        this.member = member;
    }
    public T getMember(){
        return member;
    }
}
public abstract class Member {
    public abstract void doPrinting();
}
public class Person1 extends Member {
    public String toString(){
        return "Person1";
    }

    @Override
    public void doPrinting(){

    }
}
public class test {
    public static void main(String[] args){

        Member member = new Member();
        GenericPrinter<Person1> memberPrinter = new GenericPrinter<>(); //Person1이 Member를 상속받고 있기때문에 쓸수있음 다른 클래스는 T자리에 쓸 수 없음
        memberPrinter.setMember(member);

        Member member = memberPrinter.getMember();

    }
}

<? super T> 사용하기

  • 상속관계에 존재하는 클래스만 자료형으로 받고 싶을때 사용한다.
  • T 의 부모이거나 T 인 자료형만 ? 자리에 올 수 있다.
  • super는 T에 사용이 불가하다.

<? super T> 를 사용한 프로그래밍

// Person 클래스
class Person {
    String name;

    Person() {
    }

    Person(String name) {
        this.name = name;
    }

    public String toString() {
        return name;
    }
}

// Person 상속 Man 클래스
class Man extends Person {
    ...
}

// Person 상속 Woman 클래스
class Woman extends Person {
    ...
}

public class WildSuper {

    public static void main(String[] args) {

        // Person
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("사람"));
        personList.add(new Person("인간"));

        // Man
        List<Man> manList = new ArrayList<Man>();
        manList.add(new Man("man1"));
        manList.add(new Man("man2"));

        // Woman
        List<Woman> womanList = new ArrayList<Woman>();
        womanList.add(new Woman("woman1"));
        womanList.add(new Woman("woman2"));
//      printData(womanList); → Man 클래스의 상위 클래스가 아니기 때문에 메소드 호출 불가

    }

    // Man 클래스와 그 상위 클래스로 생성된 인스턴스만 매개변수로 전달 가능
    public static void printData(List<? super Man> list) {
        for (Object obj : list) {
            System.out.println(obj);
        }
    }

}

< ? > 사용하기

  • < ? extends Object > 의 줄임 표현이다.
  • 어떤 자료형의 객체도 매개변수로 받겠다는 뜻이다.
  • Unbounded WildCard라고 알려져 있다.

< ? > 를 사용한 프로그래밍

public class WildTest {

    public static void main(String[] args) {
        // List는 인터페이스이기 때문에 ArrayList 생성 후 Upcasting 이용
        List<String> list = new ArrayList();
        list.add("test1");
        list.add("test2");
        list.add("test3");

        // Integer 자료형 list2 객체 생성
        List<Integer> list2 = new ArrayList();
        list2.add(1);
        list2.add(2);
        list2.add(new Integer(3));

        // Double형 list3 생성
        List<Double> list3 = new ArrayList();
        list3.add(10.1);
        list3.add(11.2);
        list3.add(12.3);

        // static 메소드 호출
        printData(list);
        printData(list2);
        printData(list3);

    }

    // 리스트 출력 메소드
    public static void printData(List<?> list) {
        for (Object v : list) {
            System.out.println(v);
        }
    }
}

extends vs super

  • 이펙티브 자바에서는 producer-extends, consumer-super 라는 말이있다.
  • 즉 제공할때는 extends 를 사용하고, 사용할 때는 super 를 사용하라는 의미이다.
public void giveMeat(Cage<? extends Carnivore> cage, Meat meat) {
    List<Carnivores> cs = cage.getAll();
}
Cage<? super Tiger> ctt = ct;
ctt.push(new Tiger());

제네릭 메서드

  • 자료형 매개변수를 메서드의 매개변수나 반환 값으로 가지는 메서드
  • 자료형 매개 변수가 하나 이상인 경우도 있음 (ex. <T, E>)
  • 제네릭 클래스가 아니어도 내부에 제네릭 메서드는 구현하여 사용할 수 있음 (ex. public methodName(T a))
  • 예시는 아래와 같다.
public class Point<T, V> {
    T x;
    V y;

    Point(T x, V y){
        this.x = x;
        this.y = y;
    }

    public T getX() {
        return x;
    }

    public V getY(){
        return y;
    }
}
public class GenericMethod{
    public static <T, V> double makeRectangle(Point<T, V> p1, Point<T, V> p2) {
        double left = ((Number)p1.getX()).doubleValue();
        double right = ((Number)p2.getX()).doubleValue();
        double top = ((Number)p1.getY()).doubleValue();
        double bottom = ((Number)p2.getY()).doubleValue();

        double width = right - left;
        double height = bottom - top;

        return width * height;
    }

    public static void main(String[] args){

        Point<Integer, Double> p1 = new Point<Integer, Double>(0, 0.0);
        Point<Integer, Double> p2 = new Point<>(10, 10.0);

        double rect = GenericMethod.<Integer, Double>MakeRectangle(p1,p2); //사용할때 변수를 정의해 준다.
    }
}

REFERENCES

'Java' 카테고리의 다른 글

Stream 응용  (0) 2022.03.06
함수형 인터페이스  (0) 2022.02.26
접근 지정자  (0) 2022.02.26
인터페이스 기본 메소드와 스태틱 메소드  (0) 2022.02.26
상수와 리터럴  (0) 2022.02.26