Java

[Java Programming] 11.1 제네릭

코딩펭귄 2024. 1. 31. 17:48

제네릭 & 컬렉션 프레임워크

- 자바 1.5부터 추가됨 (15년 전)

- 유연한 데이터 처리를 가능케 하는 Java의 강력한 기능 중 하나

- 자바로 개발을 할 때 안쓰면 안됨. 반드시 알고 있어야됨

- 완전히 중복된 코드인데, 타입만 다를경우 유용하게 사용할수있음

 

제네릭

자료구조의 형태만 제시하고 실제로 어떤 타입의 데이터가 관리될 것인지나중에 결정하는 기술

 

컬렉션

제네릭을 이용해 Array를 조금 더 다양한 형태로 간편하게 사용할 수 있도록 제공하는 프레임워크


제네릭

- 유연한 자료구조를 만들어낼 수 있음

- 클래스 / 인터페이스 / 메소드 정의할 때  type 을 parameter로 사용할 수 있도록 함

- 사용할 타입을 나중에 정하도록 하는 제네릭을 사용하면 훨씬 더 편리해짐

 

제네릭 타입

- 타입을 파라미터로 가지는 클래스와 인터페이스 <> 사이에 타입파라미터 위치함

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

 

 

 

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

- 제네릭은 클래스명 옆에 부등호( < > )를 이용해 정의.

  <제네릭 타입변수명(들) 선언> , < T > = 어떤타입인지 일단은 모르지만, 데이터가 들어오면 그 타입으로 T를 싹 바꿔준다

- 일반적으로 영문 대문자 한글자를 사용한다

제네릭 타입변수 의미
T 타입
K
V
N 숫자
E 원소

 

2. 제네릭 클래스의 객체생성

- 일반 클래스의 객체생성과정과 비슷하지만, 클래스명 다음에 <실제 제네릭 타입> 삽입한다는점에서 차이가있음

= > 객체 생성시 제네릭 타입 변수에 실제 타입을 대입함

클래스명 <실제 제네릭 타입> 참조변수명 = new 클래스명<실제 제네릭 타입>();
or
클래스명 <실제 제네릭 타입> 참조변수명 = new 클래스명<>();

 

3. 제네릭 메서드의 정의와 호출

- 일반 클래스 내부의 특정 메서드만 제네릭으로 선언하는것

- 리턴타입 or 입력매개변수의 타입을 제네릭타입 변수로 선언함

- 제네릭 메서드는 호출되는 시점에 실제 제네릭 타입을 지정함 (제네릭 클래스 : 객체 생성하는 시점에 실제 타입 지정)

 

제네릭메서드 문법구조

 

제네릭타입 변수명이 1개

접근지정자 <T> T 메서드명 (T t){
}

 

제네릭타입 변수명  2개

접근지정자 <T, V> T 메서드명 (T t, V v){
}

 

매개변수에만 제네릭 사용됐을때

접근지정자 <T> void 메서드명 (T t){
}

 

리턴타입에만 제네릭 사용

접근지정자<T> T 메서드명 (int a){
}

 

제네릭메서드 호출의 문법구조
참조객체.<실제 제네릭 타입> 메서드명(입력매개변수);

 

 

598

 

제네릭을 사용하지 않은 ScoreList
public class DynamicArray {

	public static void main(String[] args) {
		
		int[] scoreArray = new int[10];
		scoreArray[0] = 100;
		scoreArray[1] = 90;
		scoreArray[2] = 80;
		scoreArray[3] = 70;
		scoreArray[4] = 60;
		scoreArray[5] = 50;
		scoreArray[6] = 40;
		scoreArray[7] = 30;
		scoreArray[8] = 20;
		scoreArray[9] = 10;
		//scoreArray에 값 하나를 더 넣으려면 어떻게 해야 하나?
		// 학급의 학생 : 10명, 선생은 학생 10명의 학생점수를 관리하고있었다
		// int[] scoreArray = new int[10]; 이라고 썼을것
		// 학기가 종료되기 전에 1명의 학생이 전학을 왔다. 
		// -> 11명의 학생점수를 관리해야한다 -> 어떻게 해야할까?
		
		//방법1. 실패
		int[] scoreArray2 = new int[scoreArray.length + 1];
//		scoreArray2 = scoreArray;
		
		/*
		 * 배열에 데이터 추가하는 방법
		*/
		// System.arraycopy (원본배열, 원본배열에서 몇번째부터 복사할건지, 원본배열을 받을 새로운 배열, 
		// 새로운배열의 몇번째부터 복사할것인가, 원본배열에서 어디까지 복사할것인지)
		System.arraycopy(scoreArray, 0, scoreArray2, 0, scoreArray.length); //아래 3줄을 한줄로 처리
		for(int i = 0; i < scoreArray.length; i++) {
			scoreArray2[i] = scoreArray[i];
		}

		for(int score : scoreArray2) {
			System.out.println(score);
		}

		System.out.println(scoreArray2.length); // 1
		// 배열은 레퍼런스타입, 래퍼런스타입은 메모리를 들고다니므로 메모리 자체를 복사한것
		// 즉, 메모리인덱스 한개를 socreArray2에 전달해준것 (int[] scoreArray2 = new int[2];)

		//방법2. 성공
		scoreArray2[0] = scoreArray[0];
		System.out.println(scoreArray2.length); //2
		System.out.println(scoreArray[0]);	//100	
		System.out.println(scoreArray2[0]); //100
}
제네릭을 사용한 ScoreList
public class ScoreList<T> {

	// 여기에 T가아닌 object 쓰는이유 : 여기에 T 쓰면 3줄밑의 코드에서도 new T[2]라고 해야되는데, 생성자는 생성할때 타입 명시해줘야되기 때문    
    private Object[] scoreArray; //object : 어떤타입이나 들어갈 수 있음
    private int size;
    public ScoreList() {
        scoreArray = new Object[2]; //생성자는생성할때 타입 명시해줘야하므로(<-T라고 쓰면 안되는 이유!)
    }
    /**
     * scoreArray 배열에 값을 추가합니다.
     * @param score
     */
    public void add(T score) {
        if ( size >= scoreArray.length ) {
            Object[] tempScoreArray = new Object[scoreArray.length + 2];
            … 생략 …
        }
        … 생략 …
    }
    
    /**
     * scoreArray 배열에서 index의 값을 반환합니다.
     * @param index
     * @return
     */
    public T get(int index) {
        if (index >= size) {
            throw new IndexOutOfBoundsException(index);
        }
        return (T) scoreArray[index]; // object타입을 integer로 형변환시켜주기 위함
    }
	… 생략 …}

- 제네릭에는 레퍼런스타입, 즉 클래스 데이터만 쓸 수 있음 (int타입 쓰고싶다면 <Integer> 이라고 써줘야됨)

 

 

- 자바 1.7부터 문법 변경되어 앞쪽 타입에만 Integer 적어주면 됨

  ScoreList<Integer> score = new ScoreList<>();

 

<T> : 제네릭은 보통 한 글자 단위로 작성
public class ScoreList<T>
- 의미는 부여하기 나름으로 T는 Type의 앞 글자만 사용한것. 다른 타입을 사용해도 문제는 없음
public void add(T score)
- 파라미터의 T는 클래스에 정의한 T와 동일
- 만약, 클래스 제네릭에 String이 전달되었다면  add 메소드의 T 또한 String이 됨
public T get(int index)
- 리턴타입의 T는 클래스에 정의한 T와  동일
- 만약, 클래스 제네릭에 String이 전달되었다면  get 메소드의 T 또한 String이 됨

 

 

 

 

 

인터페이스부터 제네릭 만들어나가야함