자바

제네릭

Atriel 2023. 12. 19. 15:00

jdk 1.5 부터 제공하며

내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다.

 

하나의 값이 여러 다른 데이터 타입들을 가질 수 있는 기술에
중점을 두어 재사용성을 높일 수 있는 프로그래밍 방식

 

장점으로는

1. 컴파일 시 제네릭 코드에 대해 강한 타입 체크를 함
    • 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거
      할 수 있음

2. 컬렉션, 람다식, 스트림, NIO에서 널리 사용됨


3. 불필요한 타입변환을 제거

    • 제네릭 코드로 타입을 지정해 놓으면 해당 타입으로 사용할
      때 타입변환을 하지 않아도 됨

 

단점은.. 문법이 어렵다 정도?

익숙해 지기만 하면 매우 편함

 

 

 

제네릭 멀티 타입 파라미터

두 개 이상의 멀티 타입 파라미터 사용 가능

public class 클래스명<T,M> { ... }
public interface 인터페이스명<T,M> { ... }

클래스명<사용할타입1,사용할타입2> 참조변수명 = new 클래스명();

 

쓸일 없음 컬렉션이 그냥 이렇게 생김

 

 

제네릭 메소드

매개타입과 리턴 타입으로 타입 파라미터를 갖는 메소드
리턴 타입 앞에 < 타입 > 기호를 추가

public <T> 리턴타입 메소드명(매개변수,...) { ... }

<사용할 타입>메소드명(인자값);
메소드명(인자값);

* 사용할 타입을 생략하면 인자값의 타입을 보고 타입을 추정함

 

 

제네릭 제한된 타입 파라미터

타입 파라미터로 지정되는 타입을 제한하기 위한 용도로 사용
타입 파라미터 뒤에 extends 키워드를 붙이고 상위타입을 명시

public <T extends 상위타입> 리턴타입 메소드(매개변수){...}

 

상위타입을 제한거는일은 거의 할일 없음

 

 

 

제네릭 와일드 카드 타입 파라미터

 

코드에서 ?를 일반적으로 와일드 카드라고 부름

 

제네릭 와일드 카드 타입 사용 범위

제네릭 타입 <?> : 제한없음
	(모든 클래스나 인터페이스 타입 사용 가능)
    
제네릭 타입 <? extends 상위타입> : 상위클래스 제한
	(명시된 상위타입이나 하위타입만 사용 가능)
    
제네릭 타입 <? super 하위타입> : 하위클래스 제한
	(명시된 하위타입이나 상위타입만 사용 가능)

 

 

 

제네릭 타입의 상속과 구현

제네릭 타입의 클래스나 인터페이스를 상속 받을 경우 자식 클래스도 제네릭
타입으로 정의

public class ChildProduct<T,M> extends Product<T,M>{...}
public class ChildProduct<T,M,C> extends Product<T,M>{...}

 

[GenericBasic.java]

package com.multi.ex01.generic;

import java.util.Date;

public class GenericBasic {
	public static void main(String[] args) {
		// Type을 정하지 않고 Object 배열 선언하는 경우의 문제점
		// 1. 지정된 Type이 아닌 다른 Type에 대한 Check가 항상 필요했다. instanceof
		// 2. 객체를 제대로 활용하기 위해선 casting 필요했다.
		
		Object[] array = new Object[10];
		array[0] = "홍길동";
		array[1] = new Date();
		array[2] = 4;
		
		for(Object o : array) {
			if(o instanceof Date) {
				System.out.println("날짜 : " + o);
			} else if(o instanceof String) {
				System.out.println("문자열 : " + o);
				System.out.println(((String)o).toUpperCase());  //안됨
			}
		}
		
		
		MyGenericArray<Member> memberArray1 = new MyGenericArray<Member>(10); //1.8버전 까지 문법 <Member>
		MyGenericArray<Member> memberArray2 = new MyGenericArray<>(10); //1.8버전 이후 <> 정석문법
		MyGenericArray<Member> memberArray3 = new MyGenericArray(10); // 패러미터를 아예 생략하면 warning
		
		memberArray2.add(new Member("test1", "홍길동", 31));
		memberArray2.add(new Member("test2", "김길동", 21));
		memberArray2.add(new Member("test3", "최길동", 25));
		
		Member m1 = memberArray2.get(0); //type 캐스팅이 필요없다
		Member m2 = memberArray2.get(1);
		Member m3 = memberArray2.get(2);
		
		System.out.println(m1 + "\n" + m2 + "\n" + m3);
		
		// Generic 상속 문법 1 - ?(와일드카드) 표현법
		// 잘안씀 알고만있자
		//Number가 부모인 Class에만 해당 배열을 넣을수 있음
		
		MyGenericArray<? super Number> numberArray = new MyGenericArray<>(10);
		numberArray.add(new Integer(4));
		numberArray.add(31); // auto boxing!
		numberArray.add(new Float(3.14f));
		numberArray.add(new Double(3.14121592));
//		numberArray.add(new String("홍길동")); //error: 숫자가아님(Number)
		
		System.out.println(numberArray.get(0));
		System.out.println(numberArray.get(1));
		System.out.println(numberArray.get(2));
		System.out.println(numberArray.get(3));

		// Generic 상속 문법 2 - 일반적인 문법, super와 extends를 쓰지 않아도 문제가 없다.
		
		MyGenericArray<Number> numberArray2 = new MyGenericArray<>(10);
		numberArray2.add(new Integer(4));
		numberArray2.add(31); // auto boxing!
		numberArray2.add(new Float(3.14f));
		numberArray2.add(new Double(3.14121592));
//		numberArray2.add(new String("홍길동")); //error: 숫자가아님(Number)
	}
}

 

 

[MyGenericArray.java]

package com.multi.ex01.generic;


// 제네릭 기반 Class
public class MyGenericArray <T> { // T는 약속된 문자로, 아무 문자열로 대체 가능!
	// 제네릭이란? Type을 사용자가 외부에서 주입(injection) 시켜 정의하는 문법
	// T는 Type의 약자로 활용, <T>로 외부에서 타입을 주입시켜 내부에 Type으로 대체된다.
	// T는 정해져 있지 않은 상태에서 미리 프로그래밍이 가능하고, 'T'로 안해도 되고 사용자가 정해도 된다. 

	private Object[] array;
	private int index = 0;
	
	public MyGenericArray(int size) {
		array = new Object[size];
	}
	
	public int add(T data) {// 여기서의 T가 나중에 사용자가 정의할 Type이 된다.
		if(index >= array.length) {
			return -1;
		}
		array[index++] = data;
		return index;
	}
	
	public T get(int index) {
		if(index < 0 || index >= this.index) {
			return null;
		}
		return (T) array[index];
	}
	
	
	
	
}