package ex0729;

public class Ex01 {
	public static void main(String[] args) {
		Test1<Integer> t = new Test1<>();
		
		t.append(10);
		t.append(20);
		t.append(30);
		
		// t.append("자바"); // 컴파일 오류
		
		Integer i = t.get(2);
		System.out.println(i);
	}
}

class Test1<E> {
	private Object[] data;
	private int count;

	public Test1() {
		data = new Object[10];
	}

	public void append(E e) {
		if(count >= data.length) {
			// ArrayIndexOutOfBoundsException() : unchecked 예외. 배열의 첨자가 초과한 경우 발생
			throw new ArrayIndexOutOfBoundsException("요소의 개수를 초과 했습니다.");
		}
		
		data[count++] = e;
	}
	
	@SuppressWarnings("unchecked") // 부적절한 컴파일러의 경고를 제거하기 위해 사용
	public E get(int index) {
		if(index >= count) {
			throw new ArrayIndexOutOfBoundsException(index);
		}
		
		// 제네릭으로 casting 하면 경고가 발생
		return (E) data[index];
	}
	
	public int getCount() {
		return count;
	}
	
}

@SuppressWarnings("unchecked") // 부적절한 컴파일러의 경고를 제거하기 위해 사용

하지만 정확하게 알 때만 사용. 거의 사용하지 않는다!!

<위 코드는 아래와 비교해서 이해할 것!>

package ex0729;

public class Ex02 {
	public static void main(String[] args) {
		Test2<Integer> t = new Test2<>();
		t.append(10);
		t.append(20);
		t.append(30);

		// Integer[] i = t.get(); // 런타임 오류. ClassCastException. 배열은 강제 형변환이 안됨.

		// Cast가 필요
		Object[] oo = t.get();
		for (Object o : oo) {
			Integer i = (Integer) o;
			System.out.println(i);
		}
		// 제네릭배열은 의미가 없다.
	}
}

class Test2<E> {
	private E[] data;
	private int count;

	@SuppressWarnings("unchecked")
	public Test2() {
		// 제네릭 배열 메모리 할당
		// data = new E[10]; // 컴파일 오류
		data = (E[]) new Object[10]; // 제네릭은 Object로 메모리할당을 해야 함.
	}

	public void append(E e) {
		if (count >= data.length) {
			throw new ArrayIndexOutOfBoundsException("배열 요소의 개수를 초과 했습니다.");
		}

		data[count++] = e;
	}

	public E[] get() {
		return data;
	}

	public int getCount() {
		return count;
	}
}

위에서는 Object로 배열을 만들어서 생성자에서 배열의 메모리 할당을 해주었는데,

밑에서는 제네릭클래스에서 제네릭 배열을 만들었다. 

배열의 선언을 위해 생성자에서 할당하려고 했을 때 

data = new E[10]; 은 컴파일 오류가 발생했다. 메모리 할당을 위해서는 Object로 해야 했다. 그래서 배열 메모리 할당 후에 data에 다시 제네릭타입으로 캐스팅하고 넣었다. 여기서 느낌표가 발생해서 다시 

@SuppressWarnings("unchecked") 를 넣어줬다.

 

메인 클래스에서 제네릭타입을 Integer로 해서 객체를 생성한 후 Integer 값들을 3개를 넣었는데 그 배열을 다시 만들으려 했으나

Integer[] i = t.get(); // 런타임 오류. ClassCastException. 배열은 강제 형변환이 안된다.

결국 이 배열을 받기 위해서는 Object로 배열을 생성해서 받아야 했다.

 

package ex0729;

public class Ex03 {
	public static void main(String[] args) {
		Test3<Integer> t = new Test3<>();
		
		Integer[] i = {10,20,30};
		
		t.set(i);
		
		Integer[] i2 = t.get();
		System.out.println(i2[1]);
		
	}
}

class Test3<E> {
	private E[] data;
	
	public void set(E[] data) {
		this.data = data;
	}
	
	public E[] get() {
		return data;
	}
}

 

 

package ex0729;

public class Ex04 {
	public static void main(String[] args) {
		Test4<Number> ob1 = new Test4<>();	
		ob1.set(new Integer(30)); // 타입 매개변수의 상속 관계는 성립
		System.out.println(ob1.get());
		
		// Number n = ob1.get();
		// Integer i = ob1.get(); // 컴파일 오류
		// Integer i = (Integer)ob1.get();
		
		// Number n = new Integer(30); // 업 캐스팅
		Test4<Integer> ob2 = new Test4<>();
		ob2.set(new Integer(30));
		// Test4<Number> ob3 = ob2; // 컴파일 오류. 제네릭은 업캐스팅 불가.
		
		
	}
}

class Test4<T> {
	private T t;
	
	public void set(T t) {
		this.t=t;
	}
	
	public T get() {
		return t;
	}
}

제네릭 타입을 Number로 주었기 때문에 

Integer i = ob1.get(); 은 컴파일 오류이고 강제적으로 캐스팅을 한 후에는 가능하다.

Integer i = (Integer)ob1.get();

Test4<Number> ob3 = ob2; 제네릭은 업캐스팅이 불가해서 컴파일 오류이다.

 

제네릭은 컴파일 단계에서 타입 안정성을 위해 지원하므로 기본적으로 다형성을 갖지 않는다. 

package ex0729;

public class Ex05 {
	public static void main(String[] args) {
		Test5<Integer> ob = new Test5<>();
		ob.set(30);
		
		// Test5<Number> ob2 = ob; // 컴파일 오류. 상속관계가 아님.
		// 제네릭은 같은 데이터 타입만 가능. 훨씬 안정적이다.
		
		// 제네릭 - 와일드 카드
		// ? : 모든 클래스나 인터페이스가 가능
		// 제네릭 타입에 의존적이지 않는 메소드등을 호출
		Test5<?> ob2 = ob;
		ob2.print();
		
		// ob2.set(new Integer(30) ); // 컴파일 오류. 자료형이 결정되지 않은 상태이므로
	}
}

class Test5<T> {
	private T t;
	
	public void set(T t) {
		this.t=t;
	}
	
	public T get() {
		return t;
	}
	
	public void print() {
		System.out.println(t);
	}
}

제네릭에 다형성을 갖도록 만들기 위해 와일드 카드를 사용한다.

제네릭 타입의 클래스를 파라미터로 사용할 때 구체적인 타입이 정해지지 않는 경우에 사용한다.

 

제네릭타입<?> 

  • 제한 없다.
  • 타입 파라미터를 대치하는 구체적인 타입으로 ?에는 모든 클래스나 인터페이스 타입이 올 수 있다.

Test5<?> ob2 = ob;
ob2.print(); // 제네릭 타입에 의존적이지 않은 메소드는 호출할 수 있지만 

ob2.set(); // 이것과 같은 제네릭 타입이 들어가는 메소드는 호출할 수 없다. 컴파일 오류,

'쌍용강북교육센터 > 7월' 카테고리의 다른 글

0729_Ex06~Ex09_system : Java API  (0) 2021.07.29
0729_ConsoleEx_콘솔 입력  (0) 2021.07.29
0728_Ex11~Ex17_generic : 제네릭  (0) 2021.07.29
0728_Ex01~Ex03_exception : 예외처리  (0) 2021.07.29
0727_패키지  (0) 2021.07.27

+ Recent posts