Java

[Java] 11. Generics(제네릭)

Joo.v7 2024. 8. 31. 19:56

Chapter 1: Generics 개요

  • Generics 개요
  • 강력한 타입 검사
  • 타입 변환 감소
  • 알고리즘 일반화

Chapter 2: Generic 타입

  • Generic 타입 선언
  • Generic 타입 생성자
  • Generic 객체 생성

Chapter 3: Generic 메소드

  • Generic 메소드 선언
  • 타입 추론

Chapter 4: 타입 파라미터

  • 타입 파라미터 명명 규칙
  • 다중 타입 파라미터
  • 타입 제한
  • 상속 및 서브 타입
  • 와일드 카드

Chapter 5: 타입 삭제

  • Generics 에서의 타입 삭제
  • Generics 메소드에서의 타입 삭제

Chapter 6: Generics 제약

  • 타입 파라미터 제약
  • Generics 타입 제약
  • 메소드 오버로딩 제약

Lab 11-A Generics를 이용한 응용 프로그램 작성


 

Chapter 1: Generics 개요

 자바의 다형성 3가지

  1. 임의적인 다형성: 오버로딩 - 시그니처에 따라서 다르게 동작.
  2. 서브타입 다형성: 오버라이딩 - instance에 따라서 다르게 동작.
  3. 파라미터 다형성: Generics - 타입 매개변수를 사용하여 data type에 의존하지 않는 코드 작성.

 

Generics 개요

  • 객체 생성시 부여되는 타입을 이용한 파라미터 다형성 구현.
  • 타입(Class, Interface) 또는 메소드에 정의
class Box<T> {
    T item;

    public Box(T string) {
        this.item = string;
    }

    public T getItem() {
        return this.item;
    }
}
Box<String> stringBox = new Box<>(“String”);
Box<Integer> integerBox = new Box<>(100);


class Box<T> {
    T item;

    public T simpleFunction(T item) { ... }
}

 

 

강력한 타입 검사

  • Generics는 타입 안정성(Type Safety)을 지원(컴파일 타임에 검사함)
  • java.lang.Object 타입으로 변환되는 객체 형 변환은 런타임에 검사. (컴파일시 오류 발견 불가)
  • Generics는 타입이 명확히 명시되므로 컴파일 타임에 오류를 검출할 수 있음.

* Generics가 적용되지 않은 코드에서는 data를 최상위 타입인 java.lang.Object로 처리함
  (컴파일 타임에 오류를 발견할 수 없다.)

public class TypeSafetyExample {
    public static class Box {
        Object item;

        public void set(Object item) {
            this.item = item;
        }

        public Object get() {
            return this.item;
        }
    }
    public static void main(String [] args) {
        Box box = new Box();
        box.set("Hello");
        Integer value = (Integer) box.get(); // Object를 Integer로 형변환 하려고 해서 오류.
        System.out.println("Value is " + value);
    }
}

 

* Generics를 적용하면 컴파일 타임에 오류를 발견하여, 런타임 오류를 피할 수 있다.

public class TypeSafetyGenericsExample {
    public static class Box<T> {
        T item;

        public void set(T item) {
            this.item = item;
        }

        public T get() {
            return this.item;
        }
    }
    public static void main(String [] args) {
        Box<String> box = new Box<>();
        box.set("Hello");
        Integer value = box.get();
        System.out.println("Value is " + value);
    }
}

// 컴파일러 오류
$ javac TypeSafetyGenericsExample.java
TypeSafetyGenericsExample.java:16: error: incompatible types: String cannot be converted to Integer
        Integer value = box.get();

 

타입 변환 감소

  • Super type을 Sub type으로 변환하기 위해서는 타입 변환이 필요.
  • Generic을 적용하면 별도의 타입 변환 과정이 필요하지 않다.
/* Object(Super type) -> Integer(Sub type) 변환시 Type Casting이 필요. */
public class TypeSafetyExample {
    public static class Box {
        Object item;
        …
    }
    public static void main(String [] args) {
        …
        Integer value = (Integer) box.get();
        …
    }
}

/* Generic은 별도의 Type Casting이 필요치 않다. */
public class TypeSafetyGenericsExample {
    public static class Box<T> {
        T item;
        …
    }
    public static void main(String [] args) {
        …
        Integer value = box.get();
        …
    }
}

 

알고리즘 일반화

  • Generics는 알고리즘 일반화를 구현할 수 있도록 한다.
    • 다양한 타입의 객체들을 다루는 메소드나, 컬렉션 클래스에 컴파일 시 타입체크를 가능하도록 함.
  • 타입 파라미터가 Comparable의 서브타입이면 정렬 가능하도록 정의
  • 비교 가능한 아이템의 경우 정렬이 가능하도록 선언하며, 특정 데이터 타입에 국한되지 않는 일반적인 알고리즘 구현 가능.

2024.09.01 - [코딩 문제] - [코딩 문제] String Sort (with Bubble Sort)

 

[코딩 문제] String Sort (with Bubble Sort)

문제)Generic을 이용하여 String을 정렬해라. 코드import java.util.*;public class StringBubbleSort {// interface인 Comparable을 extends 하는 이유: T는 Comparable인 타입이어야 한다!! // 그래서 Comparable을 extends(확장)한

lightningtech.tistory.com

 


 

Chapter 2: Generic 타입

Generic 타입 선언

  • 일반 타입 선언(Class, Interface)에 Type Parameter를 추가한 형태로 사용.
  • Type ParameterClass나 Interface의 이름 뒤에 선언, <> 기호 사이에 열거. ex) <T>, <K, V>
  • 하나 이상의 Type Parameter를 사용 가능.
/* Generic Type의 Class인 Box */
public class Box<T> {
    private T t;

    public void set(T t) {
        this.t = t;
    }

    public T get() {
        return this.t;
    }
}

/* 하나 이상의 Type Parameter 사용 가능 */
class Pair<K, V> {
    private K key;
    private V value;

    public K getKey() {
        return this. key;
    }

    public V getValue() {
        return this. value;
    }
}

 

Generic 타입 생성자

  • Generic Type 생성자에서는 Type Parameter를 사용하지 않음.
  • 일반 클래스와 동일한 방법으로 생성자 선언.
class Box<T> {
    T box;
    
    public Box(T box) {
        this.box = box;
    }
}

// 아래와 같은 방식으로 생성
Box<Integer> intBox = new Box<Integer>(1);

// 생성자 호출 시 타입 파라미터 타입 생략 가능.
Box<Integer> intBox2 = new Box<>(1);

 

Generic 객체 생성

  • Generic 타입을 생성할 때 Type Parameter가 주어져야 함. 
  • 참조 변수생성자에 주어지는 Type Parameter는 동일해야 함.
  • Type Parameter의 상속 관계는 Generic 타입 생성시 영향을 주지 않음.
    • Type Parameter는 불공변성을 가짐.
    • Generic 타입의 상속 관계는 일반 상속관계와 동일하게 적용됨.
  • Type Parameter가 명확한 경우, 생서자 호출 시 전달되어야 하는 타입 변수는 생략 가능.
Box<Integer> box = new Box<Integer>();
Box<String> box = new Box<String>();
Box<Integer> box = new Box<>(1); // 생성자 호출시 전달되는 타입 변수 생략 가능.

Box<Integer> box = new Box<>("Hello");	// 컴파일 오류
Box<Integer> box = new Box("Hello");	// 오류 가능성 경고

 

 

 


 

Chapter 3: Generic 메소드 

Generic 메소드 선언

  • 일반 메소드 선언과 동일, 일반화 할 타입을 타입 파라미터로 선언.
  • 메소드 앞에 <> 기호를 사용하여 타입 파라미터 목록을 선언.
  • 타입 파라미터는 메소드의 return type 앞에 위치해야 한다.
<Access Modifier> <Type Parameter List> <Return Type> Method name(Parameter list)

/* 배열을 파라미터로 받아, 리스트로 반환. */
public class GenericMethod {
    public static <T> List<T> arrayToList(T[] array) { // Generic method
        List<T> list = new LinkedList<T>();

        for(T t : array) {
            list.add(t);
        }
        return  list;
    }

    public static void main(String[] args) {
        Integer[] array = {1, 2, 3};

        GenericMethod me = new GenericMethod();
        List<Integer> list = me.<Integer>arrayToList(array);
    }
}

 

타입 추론

  • 메소드 시그니처의 Type Parameter는 호출시 생략 가능.
    • 컴파일러에서 주어진 Parameter를 통해 적용될 타입의 추론이 가능.
  • 하나의 Type Parameter에 대해 추정되는 타입이 2개 이상 존재할 경우 오류 발생.
public <T> void fromArrayToCollection (T[] array, Collection<T> collection) {
    ...
}

String[] sa = new String[100];
Collection<String> cs = new ArrayList< >();

fromArrayToCollection(sa, cs);	// T는 String으로 추론됨

 

Chapter 4: 타입 파라미터

타입 파라미터 명명 규칙

  • E: 요소 (Element) - Java Collections Framewrok에서 폭 넓게 사용됨.
  • K: 키(Key)
  • N - 숫자(Number)
  • T - 타입(Type)
  • V - 값(Value)
  • S, U, V 등 - 2번째, 3번째, 4번째 등

 

다중 타입 파라미터

  • 파라미터에는 하나 이상의 타입을 지정할 수 있다.
public 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; }
}
  • 다중 파라미터 타입도 타입 인자를 통해 타입 파라미터의 추론이 가능한 경우, 생성자에서 타입 인수의 생략이 가능함.
Pair<String, Integer> p1 = new OrderPair("Even", 8);
Pair<String, String> p2 = new OrderPair("Hello", "World");

 

타입 제한

  • 타입 파라미터로 전달 가능한 함수는 참조 타입, 즉 클래스나 인터페이스로 제한됨.
  • int, boolean 등 primitive data type을 사용해야 하는 경우 Wrapper 클래스를 사용.
Pair<String, int> p1 = new OrderedPair<String, int>("Even", 8); // 컴파일 오류

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8); // 정상 동작

 

상속 및 서브 타입

  • Type Parameter의 상속 관계는 Generic 타입의 상속과 무관함.
  • Generic 파라미터로 지정된 타입 instance의 동작은 공변적이다.
/* Generic 파라미터로 전달되어 생성된 instance는 클래스의 타입 변환 규칙과 동일 */
Box<Number> box = new Box<Number>();
box.add(new Integer(10));   // OK
box.add(new Double(10.1));  // OK
  • Type Parameter는 반공변적이다.
/* Generic 파라미터 타입과 생성자에서 생성되는 Generic 파라미터 타입은 반드시 같아야 한다. */
Box<Number> box = new Box<Integer>();	// 컴파일 오류, 별개의 타입으로 인식됨.
  • Generic 클래스의 확장: Type Parameter를 변경하지 않는 한 서브 타입 지정 관계가 유지됨.
/* Generic 타입 상속 - 일반 클래스와 동일하게 확장 가능. */
public class Product<T, M> {
    private T kind;
    private M model;

    public T getKind() {
        return kind;
    }

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

    public M getModel() {
        return model;
    }

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

public class ChildProduct<T, M, C> extends Product<T, M> {
    private C company;

    public C getCompany() {
        return company;
    }

    public void setCompany(C company) {
        this.company = company;
    }
}

* Generic Type은 공변적이고, Generic Type Parameter는 불공변적이다.

// ArrayList는 List의 하위니까 가능 -> 타입은 공변적이다
List<Integer> list = new ArrayList<Integer>(); 

// List<Integer> list = new ArrayList<Short>(); 
// Short가 Integer의 하위여도 불가능 -> 타입 파라미터는 반 공변적이다.

 

와일드 카드

Generic을 정의할 때, return 값에 대한 특정한 타입 파라미터를 정의하지 않고 임의의 타입이 올 수 있는 기능을 제공한다. Generic 파라미터의 와일드 카드는 <?> 형식으로 정의되며, 인자로 받을 수 있는 타입의 제한이 가능하다.

  • 파라미터나 return 값이 특정한 Type Parameter를 정의하지 않고, 임의의 타입을 지정.
    • 타입 인자에 대한 제한을 두지 않음.
    • 특정 타입 또는 상속 받은 타입으로 타입 인자를 제한
    • 특정 타입 또는 상위 타입으로 타입 인자를 제한.
  • 무제한 와일드 카드
    • 타입 인자에 제한을 두지 않음.
void genericMethod(Collection<?> collection) { ... }
  • 슈퍼 타입 제한 와일드 카드
    • 지정된 타입이나 그로부터 상속 받은 타입에 한해서만 적용.
    • 타입 파라미터에 extends 키워드를 사용해 허용가능한 최상위 타입을 선언.
void genericMethod(List<? extends Number>) { ... }
  • 서브 타입 제한 와일드 카드
    • 지정된 타입이나 슈퍼 타입에 한해서만 적용.
    • 타입 파라미터에 super 키워드를 사용해 허용 가능한 최하위 타입을 선언.
void genericMethod(List<? super Number>) { ... }

 

참조: https://github.com/gikpreet/class-programming_with_java/blob/master/Module%2011%20Generics/contents/19_wildcard.adoc

 

class-programming_with_java/Module 11 Generics/contents/19_wildcard.adoc at master · gikpreet/class-programming_with_java

Contribute to gikpreet/class-programming_with_java development by creating an account on GitHub.

github.com

 

 


 

Chapter 5: 타입 삭제

Generics에서 Type Parameter는 컴파일 타임에만 사용되고, 컴파일 후에는 삭제 후 Object 타입으로 치환되어 byte code에는 Generic에 관한 정보가 저장되지 않는다.

 

Generics 에서의 타입 삭제

  • Generic Type Parameter는 컴파일시에 모두 삭제됨.
  • Type Parameter가 제한되지 않는 경우: Object Type으로 변경됨.
/* Generic Type Parameter */
public class Node<T> {
    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
   }
}

/* Object Type으로 변경됨 */
public class Node {
    private Object data;
    private Node next;

    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
   }
}
  • 제한된 Type Parameter 사용하는 경우: 해당 타입으로 변경됨.
/* Generic Type Parameter, 그러나 제한됨 */
public class Node<T extends Comparable<T>> {
    private T data;
    private Node<T> next;

    public Node(T data, Node<T> next) {
        this.data = data;
        this.next = next;
    }

    public T getData() {
        return data;
    }
}

/* 제한된 타입으로 대치 */
public class Node {
    private Comparable data;
    private Node next;

    public Node(Comparable data, Node next) {
        this.data = data;
        this.next = next;
    }

    public Comparable getData() {
        return data;
    }
}

 

Generics 메소드에서의 타입 삭제

  • Generic 타입과 동일하게 Type Parameter는 삭제됨.
  • 타입 제한이 없는 경우: Type Parameter는 Object 타입으로 변경됨.
/* Generic method의 Type Parameter */
public static <T> int count(T[] array, T element) {
    int count = 0;
    for (T e : array) {
        if (e.equals(element)) {
            ++count;
        }
    }
    return count;
}

/* 컴파일러가 Type Parameter를 Object로 대체 */
public static int count(Object[] array, Object element) {
    int count = 0;
    for (Object e : array) {
        if (e.equals(element)) {
            ++count;
        }
    }
    return count;
}
  • 제한된 Type Parameter를 사용하는 경우: 해당 타입으로 변경됨
class Shape { ... }
class Circle extends Shape { ... }
class Rectangle extends Shape { ... }

public static <T extends Shape> void draw(T shape) { ... }

/* 컴파일시 draw Generic 메소드의 Type Parameter인 T를 Shape로 대치함 */
public static void draw(Shape shape) { ... }

 

 


 

Chapter 6: Generics 제약

타입 파라미터 제약

  • Type Parameter 인자로 Primitive Data Type을 사용할 수 없음. (Wrapper class를 이용해야함)
    • 컴파일 과정에서 Type Parameter 정보는 모두 삭제되고, Object 타입으로 처리됨.
  • Type Parameter 변수의 객체를 생성할 수 없음.
    • Type Parameter는 타입의 인자가 주어지기 전까지는 생성할 수 없다.
  • static 필드로 선언될 수 없다.
    • Type Parameter 인자가 달라질 수 있어, 특정 타입으로 생성할 수 없다.

 

Generics 타입 제약

  • Generic 타입은 Type Casting 될 수 없으며, instanceof 연산자를 사용할 수 없다.
    • Type Parameter는 컴파일시 모두 Object로 치환됨.
    • 컴파일러에서는 Type Casting은 reference type의 경우, 상속 관계만 확인함.
    • Type Parameter가 Object 타입으로 치환되어 instanceof로 구분이 어려움.
  • Generic 타입의 배열은 생성할 수 없다. (리스트 사용해야 한다)
  • Exception 타입의 서브 타입이 될 수 없다.

https://github.com/gikpreet/class-programming_with_java/blob/master/Module%2011%20Generics/contents/25_generic_type_limitations.adoc

 

class-programming_with_java/Module 11 Generics/contents/25_generic_type_limitations.adoc at master · gikpreet/class-programming

Contribute to gikpreet/class-programming_with_java development by creating an account on GitHub.

github.com

 

메소드 오버로딩 제약

  • Type Parameter만 다른 동일한 시그니처의 메소드는 존재할 수 없다. 
    (Type Parameter는 컴파일시 모두 Object로 치환됨)
/* Type Parameter가 다른 메소드 오버로딩 */
public class Example {
    public void print(Set<String> set) { } 
    public void print(Set<Integer> set) { }
}

/* 위의 코드는 컴파일 후 아래와 같이 변경됨 */
public class Example {
    public void print(Set<Object> set) { }
    public void print(Set<Object> set) { }
}

 

 

2024.09.01 - [코딩 문제] - [코딩 문제] String Sort (with Bubble Sort)

 

[코딩 문제] String Sort (with Bubble Sort)

문제)Generic을 이용하여 String을 정렬해라. 코드import java.util.*;public class StringBubbleSort {// interface인 Comparable을 extends 하는 이유: T는 Comparable인 타입이어야 한다!! // 그래서 Comparable을 extends(확장)한

lightningtech.tistory.com

 

 

Lab 11-A Generics를 이용한 응용 프로그램 작성

 

'Java' 카테고리의 다른 글

[Java] 13. Anotation(어노테이션)  (0) 2024.09.02
[Java] 12. Lambda Expression(람다식)  (2) 2024.09.01
[Java] Singleton 패턴  (0) 2024.08.31
[Java] 10. Java에서의 상속  (0) 2024.08.30
[Java] 09. 객체 생성과 제거  (0) 2024.08.30