[Java] 제네릭

김나우·2022년 1월 16일
0

Java

목록 보기
13/15

제네릭 클래스와 제네릭 인터페이스

제네릭은 '데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터
타입들을 가질 수 있도록 하는 방법'이다

우리가 흔히 쓰는 ArrayList, HashMap 등을 생성할 때를 생각해보자
객체<타입> 객체명 = new 객체<타입>(); 이렇게 쓴다

ArrayList<Integet> list = new ArrayList<Integer>();

HashMap<String, String> map = new HashMap<String, String>();

이렇게 <>괄호 안에 들어가는 타입을 지정해준다.

이렇든 제네릭은 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에
의해 지정되는 것을 의미한다. 특정 타입을 미리 지정해주는 것이 아닌
필요에 의해 지정할 수 있도록 일반타입 이라는 것이다.

제네릭 클래스와 제네릭 인터페이스 정의하기

제네릭 클래스와 제네릭 인터페이스를 작성하는 방법은 클래스명 다음에
<제네릭 타입 변수명(들)>을 삽입하는 것이다.

==제네릭 타입 변수명이 1개 일때==
접근 지정자 class 클래스명<T>{
	//타입 T를 사용한 코드
}
접근 지정자 interface 클래스명<T>{
	//타입 T를 사용한 코드
}
==제네릭 타입 변수명이 2개일 때==
접근 지정자 class 클래스명<K,V>{
	//타입 K, V를 사용한 코드
}
접근 지정자 interface 클래스명<K,V>{
	//타입 K, V를 사용한 코드
}

제네릭 타입 변수명과 의미

제네릭 타입 변수의미
T타입(Type)
K키(Key)
V값(Value)
N숫자(Number)
E원소(Element)
public class MyClass<T>{
	private T t;
    public T get(){
    	return t;
    }
    public void set(T t){
    	this.t = t;
    }
}

public interface MyInterface<K, V>{
	public abstract void setKey(K k);
    public abstract void setValue(V v);
    public abstract K getKey();
    public abstract V getValue();
}

제네릭 클래스인 MyClass는 제네릭 타입 변수 1개를 갖고 있으며,
이 제네릭 타입은 필드타입, getter 메서드의 리턴 타입, setter 메서드의
입력 타입에 사용됐다.
제네릭 인터페이스인 MyInterface는 2개의 제네릭 타입 변수를 갖고 있으며
각 setter, getter 메서드의 입력과 리턴 타입으로 사용됐다.

제네릭 클래스의 객체 생성

일반 클래스의 객체 생성 과정과 비슷하다. 다만 클래스명 다음에
<실제 제네릭 타입>을 삽인한다는 점에서만 차이가 있다.
객체를 생성할 때 제네릭 타입 변수에 실제 타입을 대입하는 것이다.

클래스명<실제 제네릭 타입> 참조 변수명 = new 클래스명<실제 제네릭 타입>();
클래스명<실제 제네릭  타입> 참조 변수명 = new 클래스명<>();
class MyClass<T>{
    private T t;

    public T get() {
        return t;
    }

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


public class Main {
    public static void main(String[] args) {
        MyClass<String> mc1 = new MyClass<>();
        mc1.set("안녕");

        MyClass<Integer> mc2 = new MyClass<>();
        mc2.set(100);               
    }
}

제네릭 클래스는 클래스를 정의하는 시점에 타입을 지정하는 것이 아니라 객체를 생성하는 시점에 타입을 지정한다.

제네릭 타입 변수 2개를 가진 제네릭 클래스의 선언 및 활용

class KeyValue<K, V>{
    private K key;
    private V value;

    public K getKey() {
        return key;
    }

    public void setKey(K key) {
        this.key = key;
    }

    public V getValue() {
        return value;
    }

    public void setValue(V value) {
        this.value = value;
    }
}


public class Main {
    public static void main(String[] args) {
        KeyValue<String, Integer> kv1 = new KeyValue<>();
        kv1.setKey("사과");
        kv1.setValue(1000);

        ...
    }
}

제네릭 메서드

클래스 전체를 제네릭으로 선언하는 대신, 일반 클래스 내부의 특정 메서드만
제네릭으로 선언할 수 있다. 이를 '제네릭 메서드'라고 한다.
리턴 타입 또는 입력매개변수의 타입을 제네릭 타입 변수로 선언한다.
제네릭 메서드는 호출되는 시점에 실제 제네릭 타입을 지정한다.

제네릭 메서드의 문법 구조

//제네릭 타입 변수명이 1개일 때
접근 지정자 <T> T 메서드명(T t){
	//타입 T를 사용한 코드
}
//제네릭 타입 변수명이 2개일 때
접근 지정자 <T, V> T 메서드명(T t, V v){
	//타입 T를 사용한 코드
}
//매개변수에만 제네릭이 사용됐을 때
접근 지정자 <T> void 메서드명(T t){
	//타입 T를 사용한 코드
}
//리턴 타입에만 제네릭이 사용됐을 때
접근 지정자 <T> T 메서드명(int a){
	//타입 T를 사용한 코드
}

제네릭 메서드 호출의 문법 구조

참조 객체.<실제 제네릭 타입>메서드명(입력매개변수);
//일반 클래스 안에 제네릭 메서드
class GenericMethods{
    public <T> T method1(T t) {
        return t;
    }

    public <T> boolean method2(T t1, T t2) {
        return t1.equals(t2);
    }

    public <K, V> void method3(K k, V v) {
        System.out.println(k + ":" + v);
    }
}


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

        GenericMethods gm = new GenericMethods();

        //제네릭 타입을 String 으로 지정
        String str1 = gm.<String>method1("안녕");
        //입력매개변숫값으로 제넨릭 타입 유추 가능 -> 지정 생략 가능
        String str2 = gm.method1("하이");

        boolean bool1 = gm.<Double>method2(2.5, 3.5);
        boolean bool2 = gm.method2(4.5, 3.5);

        gm.<String, Integer>method3("영어", 100);
        gm.method3("수학", 80);
    }
}

제네릭 타입 범위 제한

(수정 예정)

profile
안녕하세요

0개의 댓글