제네릭 정리1

JAVA 2018. 3. 22. 22:31 |

자바 제네릭 프로그래밍은 기존 C++언어의 템플릿과 유사한 점도 있지만 차이점도 많이 갖고 있다. 이러한 차이점들은 C++ 템플릿에 비해 자바 제네릭 프로그래밍에 여러 가지 장점을 제공한다. 자바 제네릭 프로그래밍은 C++ 의 템플릿에 대해서 다음과 같은 장점을 갖는다.

  • 컴파일 시 타입 체킹 가능 - 자바 제네릭 프로그래밍은 컴파일 시에 타입 체킹이 가능하기 때문에 실행 시에 형변환에서 발생할 수 있는 많은 에러를 방지할 수 있다.
  • 하나의 컴파일 된 코드 생성 - C++의 템플릿은 실제로 사용되는 데이터에 따라 여러 개의 컴파일된 코드를 생성하는 데 비해서 자바는 하나의 컴파일된 코드를 생성한다.
  • 소스 코드 불필요 - C++의 템플릿을 사용하는 경우에 템플릿을 사용하기 위해서는 템플릿 소스 코드가 필요하지만, 자바 제네릭 프로그래밍을 사용하는 경우에는 컴파일된 라이브러리만 존재하면 된다.



  • 1. 제네릭(Generic)

    1. 작성한 코드를 다양한 타입의 객체에 대해 재사용하는 객체 지향 기법.

    2. C++ 언어의 template와 거의 유사한 기능

    3. 컬렉션, 람다식, 스트림, 네트워킹부터해서~ 안드로이드와 같은 애플리케이션을 개발할 때 많이 사용되므로 정확하게 알고 있어야한다

    4. 클래스를 정의할 때, 구체적인 타입(type)을 적지 않고 변수 형태로 적어 놓는 것이다.

    5. 클래스를 선언하여 객체를 생성할 때, 구체적인 타입을 기재한다. 즉 타입을 어떤 클래스 종류의 매개변수로 보는 것이다.

    제네릭은 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다. 

    즉, 클래스 내부에서 사용할 데이터 타입을 나중에 인스턴스를 생성할 때 확정하는 것을 제네릭이라 한다.

    객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다.

    ArrayList와 같은 컬렉션 클래스는 다양한 종류의 객체를 담을 수 있긴 하지만 보통 한 종류의 객체를 담는 경우가 더 많다. 그런데도 꺼낼 때 마다 타입체크를 하고 형변환을 하는 것은 아무래도 불편할 수 밖에 없다.

    기존의 방법 이전에는 아래와 같이 Object 타입으로 객체를 받아서 다형성을 이용했는데그 이유는 모든 객체가 Object 클래스를 상속하므로 다양한 형태의 데이터를 담을 수 있었기 때문이다.

     

    public class PrevBox { Object에 대해서 알아보기

    private Object data;


    public void set(Object data) { 

    this.data = data; 

    }


    public Object get() { 

    return data; 

    }

    }

    • , get() 메소드로 저장한 객체를 반환받을 때 원하는 타입으로 캐스팅(Casting)해야 하는 번거로움이 있었다.



    제네릭 기법

    //제네릭 클래스

    public class Box<T> { // T는 타입을 의미한다.

    private T data;


    public void set(T data) { 

    this.data = data; 

    }


    public T get() { 

    return data; 

    }

    }

    • 위의 클래스를 선언할 때아래와 같이 구체적인 타입을 기재하여 제네릭 클래스를 사용한다.

    Box<String> strBox = new Box<String>(); // String 타입만 저장한다.

    Box<Integer> intBox = new Box<Integer>(); // Integer 타입만 저장한다.

     

    제네릭 클래스(Genenric class)에서는 타입을 변수로 표시한다이것을 타입 매개변수(type parameter)”라고 하며타입 매개변수는 객체 생성 시에 프로그래머에 의하여 결정된다.


    ▶ 타입 매개변수의 표기


    제네릭 클래스는 여러 개의 타입 매개변수를 가질 수 있으나 타입의 이름은 클래스나 인터페이스 안에서 유일하여야 한다

    관례에 의하여 타입의 이름은 하나의 대문자로 한다.

    대문자로 하는 이유는 변수의 이름과 타입의 이름을 구별할 수 있게 하기 위함이다.

    아래는 일반적으로 널리 사용되는 타입의 이름들이다.

    • – Element(요소 자바 컬렉션 라이브러리에서 많이 사용된다.)

    • – Key

    • – Number

    • – Type

    • – Value

    • S, U, V 등 – 2번째, 3번째, 4번째 타입

     

    다이아몬드 자바 SE 7버전부터는 제네릭 클래스의 생성자를 호출할 때타입 인수를 구체적으로 주지 않아도 된다컴파일러가 문맥에서 타입을 추측하기 때문이다“<>”를 다이아몬드라고 표현한다.

     

    Box<String> Box = new Box<>(); //Box<String> strBox = new Box<String>(); 와 같다.

     

    - 3. 다중 타입 매개변수(Multiple Type Parameters)

    interface Pair<K, V> {
    	public K getKey();
    	public V getValue();
    }
    
    public class OrderedPair<K, V> implements Pair<K, V> {
    	private K key;
    	private V value;
     
    	public OrderedPair(K key, V value) {
    		this.key = key;
    		this.value = value;
    	}
     
    	public K getKey() { 
    		return key; 
    	}
    	
    	public V getValue() { 
    		return value; 
    	}
    }


    public class PairTest {
    	public static void main(String[] args) {
    		Pair<String, Integer> pair1 = new OrderedPair<String, Integer>(“Even”, 8);
    		Pair<String, String> pair2 = new OrderedPair<String, String>(“Hi”, “nice~”);
    		 
    		//pair1과 pair2는 인터페이서 Pair 참조 변수로 선언되었다.
    		//new OrderPair<String, Integer>은 K를 String으로 실체화하고, V를 Integer로 실체화한다.
    		//오토박싱(autoboxing)에 의하여 int(위의 값 8)가 Integer 객체로 자동 변환된다.
    		//오토박싱이란 기초 자료형을 대응되는 클래스 객체로 자동 변환해주는 기능이다.
    		 
    		//또는 아래와 같은 방식도 가능하다.
    		 
    		//Pair<String, Integer> pair1 = new OrderedPair<>(“Even”, 8);
    		//Pair<String, String> pair2 = new OrderedPair<>(“Hi”, “nice~”);
     	}
    }
    


    - Raw 타입 타입 매개변수가 없는 제네릭 클래스의 이름이다.

     

    Box<Integer> intBox = new Box<>(); //기존 방식

    Box rawBox = new Box(); //Raw 방식

    • 주의할 점은 처음부터 제네릭 클래스가 아니면 Raw 타입이라고 하지 않는다.

    • Raw 타입은 JDK 5.0 이전에는 제네릭이 없었기 때문에 이전 코드와 호환성을 유지하기 위해 등장하였다.타입을 주지 않으면 무조건 Object 타입으로 간주하는 것이다.

     

     

     

    ▶4. 제네릭 메소드

     

    일반 클래스의 메소드에서도 타입 매개변수를 사용하여 제네릭 메소드를 정의할 수 있다.

     

    제네릭 메소드에서의 타입 매개변수의 범위는 메소드 내부로 제한된다.

     

    아래는 실제 Array 클래스에 있는 제네릭 메소드의 일부이다.

     

    public class Array {

    ...

    public static <T> T getLast(T[] a)

    {

    return a[a.length-1];

    }

    ...

    }

     

    타입 매개변수(<T>)는 반드시 메소드의 수식자(public, static)와 반환형(T) 사이에 위치되어야 한다.


     public static <T> T getLast(T[] a)


    제네릭 메소드를 호출하기 위해서는 실제 타입을 꺽쇠괄호 안에 넣어주어도 되고 생략하여도 된다.

     

    String[] name = {“김철수”, “김영희”, “김숙자”, “김말년”};

    String last = Array.<String>getLast(name);

    String last = Array.getLast(name); // 컴파일러는 이미 타입 정보를 알고 있다!

     

    한정된 타입 매개변수 타입 매개변수로 전달되는 타입의 종류를 제한하기 위한 기능으로extends 키워드를 사용한다.

    • 아래는 한정된 타입 매개변수를 표현하는데 잘못된 예시이다. Array 클래스로 계속 예를 들면,

    public class Array {

    ...

    public static <T> T getMax(T[] a) {

    if (a == null || a.length = 0) return null;

     

    T largest = a[0];

     

    for(int i = 1; i < a.length; i++) {

    if (largest.compareTo(a[i]) > 0) largest = a[i];

    }


    return largest;

    }

    ...

    }

    • 위의 예시에서 T는 compareTo()라고 하는 Comparable 인터페이스를 구현해야 한다. 따라서 클래스의 범위를 Comparable 인터페이스를 구현한 클래스로 제한하는 것이 바람직하다.

    • 아래는 위의 메소드 getMax()를 올바르게 변경한 것이다.

    public static <T extends Comparable> T getMax(T[] a) {

    ...

    }

    • “T extends Comparable”은 타입 T가 Comparable 인터페이스를 구현한 클래스들에 대해서만 호출될 수 있음을 의미한다.

    • implements 키워드가 아닌 extends 키워드를 사용하는 것에 주의해야 한다. 이를 통해 타입 매개변수에 상속관계가 성립함을 추측할 수 있다.

     

     

     

    ▶ 7 .제네릭과 상속

     

    제네릭에서는 타입 매개변수에 상속관계가 성립한다.


     예를 들어 Number를 타입 매개변수로 주어 객체를 생성했다면, Number의 자식 클래스인 Integer, Double, Float 객체도 모두 처리할 수 있다. 맨 처음 작성했던 제네릭 클래스인 Box 를 사용해보자.


    public class Box<T> {

    private T date;


    public void set(T data) { 

    this.data = data; 

    }


    public T get() { 

    return data; 

    }

    }

     

    Box<Number> box = new Box<Number>(); // 객체 생성 시 구체적인 타입을 기재한다.

    box.add(new Integer(10)); // 타입의 하위 클래스들도 모두 처리된다.

    box.add(new Double(10.1));

    box.add(new Float(0.0));

     

    - 제네릭에서의 상속에서는 한 가지 주의할 점이 있다.


     타입 매개변수에서 상속관계가 성립하는 것과 어떤 타입 매개변수를 가진 제네릭 클래스에서 상속관계가 성립하는 것은 서로 다르다는 것이다.

     

    public void exMethod(Box<Number> number) { ... }

     

     위와 같은 메소드에서는 Box<Integer>와 Box<Double>과 같은 제네릭 클래스는 매개변수로 대입할 수 없다. 그 이유는 Integer 와 Double은 Number와 상속관계가 성립하지만 Box<Integer> 와 Box<Double>은 Box<Number> 와 상속관계가 성립하지 않기 때문이다. 쉽게 아래의 그림을 보면 차이를 알 수 있다.



     그러나 Box<Number>와 Box<Integer>, Box<Double> 사이에 상속관계가 성립할 수 있다.

     

    제네릭 클래스의 상속 제네릭 클래스들 간의 상속도 일반 클래스처럼 extends와 implements키워드를 사용하여 표시할 수 있다아래는 나중에 배울 컬렉션 클래스의 일부이다.


    ArrayList<E> implements List<E> { ... }

    List<E> extends Collection<E> { ... }

     

     즉Collection ← List<String> ← ArrayList<String> 순서로 부모 ← 자식 상속관계가 생긴다.

     

     

     

    ▶ 6. 와일드 카드

     

    와일드 카드(Wild Card) : 제네릭을 사용하는 코드에서 타입 매개변수를 기재하는 꺽쇠괄호 속물음표(?)로 표현되며, 카드 게임에서 조커와 유사한 역할을 한다어떤 타입이던지 나타낼 수 있다.

     

    와일드 카드는 매개변수필드지역 변수의 타입을 나타내는 등 다양하게 사용된다.

     

    상한이 있는 와일드 카드 전체 타입이 아닌 일정한 상한이 있는 타입을 표시하는데 사용된다


     코드를 작성할 때<? extends (상한)> 과 같이 작성한다예를 들어 List<Integer>, List<Double>, List<Number>에만 적용되는 메소드를 작성하고 싶다면, Integer, Double 클래스는 모두 Number 클래스를 상속받기 때문에 아래와 같이 작성하면 된다.

     

    public static void exMethod(List<? extends Number> list) { ... }

     

     List<? extends Number>의 의미는 Number를 상속받은 어떤 클래스도 자리에 올 수 있다는 것을 의미한다이는List<Number>보다는 적용 대상을 넓힌 것이다. List<Number>는 타입 매개변수로 Number에 대해서만 매치되지만 List<? extends Number>은 Number의 자식클래스까지도 타입 매개변수로 매치되기 때문이다.

     

    public static double sumOfList(List<? extends Number> list) {

    double s = 0.0;

     

    for(Number n : list) {

    s += n.doubleValue();

    }

     

    return s;

    }


    public static void main(String[] args) {

    List<Integer> li = Arrays.asList(1, 2, 3, 4, 5);

    System.out.println(“sum = ” + sumOfList(li));

    }

     

    [출력 결과]

    List<Integer> 타입의 li 가 List<? extends Number> 타입을 매개변수로 받는 sumOfList의 메소드에 적용된다.



    하한이 있는 와일드 카드 특정한 타입을 가진 모든 객체에 대해 작동할 때 사용되는 카드로<? super (하한)>와 같은 문법을 사용한다.


     예를 들어 Integer 객체를 가질 수 있는 모든 객체를 리스트에 추가하는 메소드를 작성한다면, List<Integer>, List<Number>, List<Object>와 같은 Integer 값을 가지고 있는 모든 객체에 대하여 해당 메소드를 적용시킬 수 있다.

     

    public static void addNumbers(List<? super Integer> list) {

    for(int i = 1; I <= 10; i++) {

    list.add(i);

    }

    }

     


    한도가 없는 와일드 카드 모든 타입에 매치되는 와일드 카드로, 단순히 <?> 로 표현된다. , List<?> 라고 코드를 작성하면 모든 타입의 리스트를 사용하게 되는 것이다.


     주의할 점은 List<?>와 List<Object>는 혼동할 수 있으나 차이가 있다는 것이다.

     

    public static void printList(List<Object> list) {

    for(Object element : list) {

    System.out.println(element + ", ");

    }


    System.out.println();

    }

     

     위의 경우 printList() 메소드는 Object 객체의 리스트(List<Object>)만 출력할 수 있다그 이유는List<Integer>, List<String>, List<Double>와 같은 클래스들은 List<Object>의 자식이 아니기 때문이다.


     앞에서 언급했듯이 타입 매개변수 간의 상속관계가 성립한 것이지 클래스간의 상속관계는 성립되지 않았다.


     아래는 모든 타입에 매치되는 리스트를 매개변수로 받을 수 있는 올바르게 작성된 코드이다.

     

    public static void printList(List<?> list) {

    for(Object element : list) {

    System.out.println(element + “, ”);

    }


    System.out.println();

    }

     

    ● 제네릭 클래스, 인터페이스


    자바에서 제네릭 클래스, 인터페이스, 메소드는 '<' 과 '>' 문자를 이용해서 표현한다. 예를 들어, GList라는 제네릭 클래스는 다음과 같은 형태로 정의할 수 있다. 이 때 E는 타입을 표현하기 위해서 사용되며, 포멀 파라미터 타입(Formal parameter type)이라고 한다.


    제네릭 Glist 클래스 정의

    class GLisst<E> {
        void add(E x) {
            ...
        }
        ...
    }

    위의 그림은 아래의 코드를 간략화한 것이다. 

    class Person<T>{

        public T info; // p1 일시 데이터 타입은 String이 된다.(인스턴스 생성시 String 제네릭 생성)

     // p2 일시 데이터 타입은 StringBuilder이 된다.

    }

     

    public class GenericDemo {

         public static void main(String[] args) {

            Person<String> p1 = new Person<String>();

            Person<StringBuilder> p2 = new Person<StringBuilder>();

        }

    }


    p1.info와 p2.info의 데이터 타입은 결과적으로 아래와 같다.

    - p1.info : String

    - p2.info : StringBuilder

    그것은 각각의 인스턴스를 생성할 때 사용한 <> 사이에 어떤 데이터 타입을 사용했느냐에 달려있다. 


    클래스 선언부를 보자.

    public T info;


    클래스 Person의 필드 info의 데이터 타입은 T로 되어 있다. 그런데 T라는 데이터 타입은 존재하지 않는다. 이 값은 아래 코드의 T에서 정해진다.

    class Person<T>{

    위 코드의 T는 아래 코드의 <> 안에 지정된 데이터 타입에 의해서 결정된다. 


    Person<String> p1 = new Person<String>();

    위의 코드를 나눠보자. 아래 코드는 변수 p1의 데이터 타입을 정의하고 있다.


    Person<String> p1

    아래 코드는 인스턴스를 생성하고 있다. 


    new Person<String>();

    즉 클래스를 정의 할 때는 info의 데이터 타입을 확정하지 않고 인스턴스를 생성할 때 데이터 타입을 지정하는 기능의 제네릭이다. 


    1. 제네릭의 장점

    1) 타입 안정성을 제공한다.(타입 안정성을 높인다는 것은 의도하지 않은 타입의 객체를 저장하는 것을 막고, 저장된 객체를 꺼내올 때 원래의 타입과 다른 타입으로 형변환되어 발생할 수 있는 오류를 줄여준다는 뜻이다.)

    2) 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해 진다. 

    간단히 얘기하면, 다룰 객체의 타입을 미리 명시해줌으로써 형변환을 하지 않아도 되게 하는 것이다.

    기존에는 다양한 종류의 타입을 다루는 메서드의 매개변수나 리턴타입으로 Object 타입의 참조변수를 많이 사용했고, 그로 인해 형변환이 불가피했지만, 이젠 Object타입 대신 원하는 타입을 지정하기만 하면 되는 것이다.

    (타입을 지정하지 않으면 Object 타입으로 간주된다.)


    2. 컬렉션 클래스 이름 바로 뒤에 저장할 객체의 타입을 적어주면, 컬렉션에 저장할 수 있는 객체는 지정한 타입의 객체 뿐이다.

    컬렉션클래스<저장할 객체의 타입> 변수명 = new 컬렉션클래스<저장할 객체의 타입>();

    ArrayList<Tv> tvList = new ArrayList<Tv>();

    // Tv객체만 저장할 수 있는 ArrayList를 생성

    tvList.add(new Tv());

    tvList.add(new Radio());// 컴파일 에러


    3. 다형성을 사용해야 하는 경우에는 부모타입을 지정함으로써 여러 종류의 객체를 저장할 수 있다.

    class Product{ }

    class Tv extends Product{ }

    class Audio extends Product{ }


    //Product 클래스의 자손객체들을 저장할 수 있는 ArrayList를 생성

    ArrayList<Product> list = new ArrayList<Product>();

    list.add(new Product());

    list.add(new Tv());

    list.add(new Audio());


    Product p = list.get(0);// 형변환이 필요없다.

    Tv t = (Tv)list.get(i);// 형변환을 필요로 한다.

    ArrayList가 Product타입의 객체를 저장하도록 지정하면, 이들의 자손인 Tv와 Audio타입의 객체도 저장할 수 있다. 다만 꺼내올 때 원래의 타입으로 형변환해야 한다.


    4. Product 클래스가 Tv클래스의 조상이라 할지라도 아래와 같이는 할 수는 없다.

    ArrayList<Product> list = new ArrayList<Tv>();//허용안함


    List<Tv> tvList = new ArrayList<Tv>();// But, 허용된다.


    5. 와일드카드

    보통 제네릭에서는 단 하나의 타입을 지정하지만, 와일드 카드'?'를 사용하면 된다. 보통 제네릭에서는 단 하나의 타입을 지정하지만. 와일드 카드는 하나 이상의 타입을 지정하는 것을 가능하게 해준다.

    아래와 같이 어떤 타입('?')이 있고 그 타입이 Product의 자손이라고 선언하면, Tv객체를 저장하는 'ArrayList<Tv>' 또는 Audio객체를 저장하는 'ArrayList<Audio>'를 매개변수로 넘겨줄 수 있다.

    Tv와 Audio 모두 Product의 자손이기 때문이다.

    public static void printAll(ArrayList<? extends Product> list){//Product 또는 그 자손들이 담긴 ArrayList를 매개변수로 받는 메서드

    for(Unit u : list){

    System.out.println(u);

    }

    }


    6. 복수의 제네릭

    클래스 내에서 여러 개의 제네릭을 필요로 하는 경우가 있을 것이다. 예제를 보자.

    class EmployeeInfo{

        public int rank;

        EmployeeInfo(int rank){ this.rank = rank; }

    }

    class Person<T, S>{//복수의 제네릭을 사용할 시에는 ','를 사용한다.

        public T info;

        public S id;

        Person(T info, S id){ 

            this.info = info; 

            this.id = id;

        }

    }

    public class GenericDemo {

        public static void main(String[] args) {

            Person<EmployeeInfo, int> p1 = new Person<EmployeeInfo, int>(new EmployeeInfo(1), 1);

        }

    }

    위의 코드는 예외를 발생시키지만 문제는 다음 예제에서 처리하고 형식만 보자. 

    즉, 복수의 제네릭을 사용할 때는 <T, S>와 같은 형식을 사용한다. 여기서 T와 S 대신 어떠한 문자를 사용해도 된다. 하지만 묵시적인 약속(convention)이 있기는 하다. 그럼 예제의 오류를 해결하자.


    7. 기본 데이터 타입과 제네릭

    제네릭은 참조 데이터 타입에 대해서만 사용할 수 있다. 기본 데이터 타입에서는 사용할 수 없다.(wrapper 클래스로 사용할 수 있다.--> 기본타입을 객체타입으로 만드는 것) 따라서 아래와 같이 코드를 변경한다.

    class EmployeeInfo{

        public int rank;

        EmployeeInfo(int rank){ this.rank = rank; }

    }

    class Person<T, S>{

        public T info;

        public S id;

        Person(T info, S id){ 

            this.info = info;

            this.id = id;

        }

    }

    public class GenericDemo {

        public static void main(String[] args) {

            EmployeeInfo e = new EmployeeInfo(1);

            Integer i = new Integer(10);

            Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);

            System.out.println(p1.id.intValue());

        }

    }

    new Integer는 기본 데이터 타입인 int를 참조 데이터 타입으로 변환해주는 역할을 한다. 

    이러한 클래스를 래퍼(wrapper) 클래스라고 한다. 덕분에 기본 데이터 타입을 사용할 수 없는 제네릭에서 int를 사용할 수 있다.


    8. 제네릭의 생략

    제네릭은 생략 가능하다. 아래 두 개의 코드가 있다. 이 코드들은 정확히 동일하게 동작한다. e와 i의 데이터 타입을 알고 있기 때문이다.

    EmployeeInfo e = new EmployeeInfo(1);

    Integer i = new Integer(10);

    Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);

    Person p2 = new Person(e, i);// 제네릭 생략함


    9. 메소드에 적용

    제네릭은 메소드에 적용할 수도 있다. 

    class EmployeeInfo{

        public int rank;

        EmployeeInfo(int rank){ this.rank = rank; }

    }

    class Person<T, S>{

        public T info;

        public S id;

        Person(T info, S id){ 

            this.info = info;

            this.id = id;

        }

        public <U> void printInfo(U info){// U 데이터 타입은 info라는 매개변수의 데이터타입(EmployeeInfo)이 된다.

            System.out.println(info);

        }

    }

    public class GenericDemo {

        public static void main(String[] args) {

            EmployeeInfo e = new EmployeeInfo(1);

            Integer i = new Integer(10);

            Person<EmployeeInfo, Integer> p1 = new Person<EmployeeInfo, Integer>(e, i);

            p1.<EmployeeInfo>printInfo(e);//

            p1.printInfo(e);// 제네릭 생략이 가능함

        }

    }


    10. 제네릭의 제한

    (1) extends

    제네릭으로 올 수 있는 데이터 타입을 특정 클래스의 자식으로 제한할 수 있다.

    abstract class Info{//부모 클래스가 반드시 추상클래스일 필요가 없다.

        public abstract int getLevel();

    }

    class EmployeeInfo extends Info{

        public int rank;

        EmployeeInfo(int rank){ this.rank = rank; }

        public int getLevel(){

            return this.rank;

        }

    }

    class Person<T extends Info>{// info 클래스나 info의 자식클래스만이 제네릭으로 올 수 있는 데이터 타입이 된다.(info의 자식이면 OK, 자식이 아니면 컴파일 에러)

        public T info;

        Person(T info){ this.info = info; }

    }

    public class GenericDemo {

        public static void main(String[] args) {

            Person p1 = new Person(new EmployeeInfo(1));

            Person<String> p2 = new Person<String>("부장");

        }

    }

    위의 코드에서 중요한 부분은 다음과 같다.


    class Person<T extends Info>{

    즉 Person의 T는 Info 클래스나 그 자식 외에는 올 수 없다.


    extends는 상속(extends)뿐 아니라 구현(implements)의 관계에서도 사용할 수 있다.

    interface Info{

        int getLevel();

    }

    class EmployeeInfo implements Info{

        public int rank;

        EmployeeInfo(int rank){ this.rank = rank; }

        public int getLevel(){

            return this.rank;

        }

    }

    class Person<T extends Info>{// extends는 상속이 무엇인가가 아니라, 부모가 누군가를 확인하는 코드이다.(implement가 아니다.)

        public T info;

        Person(T info){ this.info = info; }

    }

    public class GenericDemo {

        public static void main(String[] args) {

            Person p1 = new Person(new EmployeeInfo(1));

            Person<String> p2 = new Person<String>("부장");

        }

    }


    'JAVA' 카테고리의 다른 글

    [JAVA] Day_06. Member inner class & Anonymous inner class로 바꾸기  (0) 2018.03.26
    제네릭 정리2  (0) 2018.03.22
    제네릭  (0) 2018.03.22
    내부 클래스 2  (0) 2018.03.22
    내부 클래스  (0) 2018.03.22
    Posted by 너래쟁이
    :