제네릭2(generic)

Mia Lee·2022년 1월 17일
0

JAVA

목록 보기
94/98
package ex_generic;

public class Ex1 {

	public static void main(String[] args) {

		/*
		 * 사용자 정의 클래스에 대한 제네릭 타입 적용
		 * 
		 */
		
		NormalIntegerClass nic = new NormalIntegerClass();
		nic.data = 10; // 정수 저장 가능
//		nic.data = 3.14; // 실수 저장 불가능
//		nic.data = "홍길동"; // 문자열 저장 불가능
		// => 여러 데이터타입 데이터를 모두 저장하려면 최소한 Object 타입 변수 선언해야함
		
		NormalObjectClass noc = new NormalObjectClass();
		noc.data = 10;
		noc.data = 3.14;
		noc.data = "홍길동";
		// => Object 타입 변수는 모든 데이터타입 저장 가능
		// => 단, 객체 내의 데이터를 꺼내서 사용할 때 타입 판별이 필수!
//		System.out.println(noc.data + 10); // 오류 발생!
		if (noc.data instanceof Integer) {
			System.out.println((int) noc.data + 10);
		}
		
		// -------------------------------------------------------------------
		
		// 제네릭을 사용한 클래스의 인스턴스 생성
		// => 제네릭 타입 지정 시 반드시 클래스 타입(참조형)으로 명시!
		// 1. 제네릭 타입 T를 Integer 타입으로 지정
		GenericClass<Integer> gc = new GenericClass<Integer>();
		// => GenericClass 클래스 내의 모든 T 타입이 Integer 타입으로 변경됨
		gc.setData(10);
//		gc.setData("홍길동"); // 오류 발생! Integer 타입만 전달 가능!
		
		System.out.println(gc.getData()); // 리턴타입이 Integer 타입으로 바뀌어 있음
		int num = gc.getData();
		System.out.println(num);
		
		// 2. 제네릭 타입 T를 Double 타입으로 지정
		GenericClass<Double> gc2 = new GenericClass<Double>();
		// => GenricClass 클래스 내의 모든 T 타입이 Double 타입으로 변경됨
		gc2.setData(3.14);
//		gc2.setData("홍길동"); // 오류 발생!
		double dNum = gc2.getData();
		System.out.println(dNum);
		
		System.out.println("==================================================");
		
		// 제네릭타입 T를 Person 타입으로 지정 인스턴스 생성(GenericClass)
		// Setter 호출하여 Person 객체 전달하고(각자 이름, 나이)
		// Getter 호출하여 Person 객체 내의 데이터를 출력(ex. 박장범, 20)
		GenericClass<Person> gc3 = new GenericClass<Person>();
//		Person p = new Person("박장범", 20);
//		gc3.setData(p);
		gc3.setData(new Person("박장범", 20));
//		gc3.setData("박장범"); // 오류 발생!
		
//		Person p = gc3.getData();
//		System.out.println(p.getName() + ", " + p.getAge());
		System.out.println(gc3.getData().getName() + ", " + gc3.getData().getAge());
		
		System.out.println("==========================================");
		
		System.out.println(gc3.getData()); // toString() 메서드 생략!
		
	}

}

/*
 * 제네릭을 사용한 클래스 정의
 * - 클래스 정의 시점에서 클래스명 뒤에 <> 기호를 쓰고, 기호 사이에 '가상의 데이터타입' 명시
 *   => 보통 1글자 영문자 사용(주로 E(Element), T(Type) 등을 사용)
 *   => 가상의 데이터타입이므로 실제 데이터타입으로 사용은 불가능하나 제네릭 타입에서 '임시'로 설정하여 관리함
 * - 지정된 가상의 자료형은 클래스 내부에서 실제 데이터타입 명시하는 부분에 대체가 가능함
 * 
 */
class GenericClass<T> {
	// 제네릭타입 T 지정 시 클래스 내의 데이터타입 부분을 T로 지정하여 임시 데이터타입으로 클래스 정의 가능
	// (실제 사용 가능한 데이터타입은 아니다!)
	// => 차후, 객체 생성 시점에서 제네릭타입에 대한 실제 데이터타입을 명시할 경우
	//    현재 클래스 내의 제네릭타입(T) 부분이 지정된 데이터타입으로 대체됨
	// => 실제 인스턴스 생성 시점에서 어떤한 데이터타입으로도 변경이 가능!
	private T data;
	// 멤버변수의 데이터타입이 제네릭타입 T이므로 멤버변수에 접근하는 Getter/Setter 등의 데이터타입도
	// 제네릭타입 T로 지정됨
	
	public T getData() {
		return data;
	}
	
	public void setData(T data) {
		this.data = data;
	}
	
}

// Person 클래스 정의
// 멤버변수 : 이름, 나이 => 접근제한자 private
// 생성자 정의 => 멤버변수의 데이터를 전달받아 초기화
// 멤버변수에 대한 Getter / Setter
class Person {
	private String name;
	private int age;
	
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return name + ", " + age;
	}
	
	
	
}

// 제네릭을 적용하지 않는 일반 클래스 정의
// 1) 사용할 데이터타입을 특정 타입으로 관리하는 일반 클래스
class NormalIntegerClass {
	int data; // data 변수에는 정수형 데이터만 저장이 가능함
}

// 2) 사용할 데이타입을 Object 타입으로 관리하는 일반 클래스
class NormalObjectClass {
	Object data; // data 변수에는 모든 데이터타입 데이터 저장 가능 
}








package ex_generic;

public class Ex2 {

	public static void main(String[] args) {

		/*
		 * 제네릭 타입 사용 시 주의사항
		 * 1. static 멤버 내에서 제네릭 타입 파라미터 사용 불가
		 *    => 제네릭 타입은 인스턴스 생성 시점에서 실제 데이터타입으로 변환되는데
		 *       static 멤버는 인스턴스 생성 시점보다 먼저 로딩(클래스 로딩 시점)되므로 
		 *       데이터타입이 지정되지 않은 상태이기 때문에 사용이 불가능함!
		 * 2. new 연산자 사용 시 제네릭 타입 파라미터 사용 불가
		 * 3. instanceof 연산자 사용 시 제네릭 타입 파라미터 사용 불가  
		 * 
		 */
		
	}

}

class GenericClass2<T> {
	
	private T data;
	
//	private static T staticMember; // 오류 발생! static 멤버변수에 제네릭타입 파라미터 사용 불가!
	// => 인스턴스 생성 시점보다 먼저 메모리에 로딩되므로 타입 변경 불가능
	
//	public static void staticMethod(T data) {} // 오류 발생!
	
//	T instance = new T(); // 인스턴스 생성 시 제네릭타입 파라미터로 생성자 호출 불가!
	// => 컴파일 시점에서 생성자 타입이 확인 불가능하므로 사용할 수 없다!

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}
	
	public void compare() {
		Object o = new Object();
		
//		if (o instanceof T) {} // instanceof 연산자에 제네릭타입 파라미터 사용 불가!
		// => 컴파일 시점에서 T의 데이터타입 확인이 불가능하므로 true, false를
		//    미리 판별할 수 없으며, 형변환 등의 수행이 불가능!
		
	}
	
}





package ex_generic;

public class Ex3 {

	public static void main(String[] args) {

		/*
		 * 제네릭 타입의 상속과 구현
		 * 
		 */
		
		GenericClass3<Integer> gc; // Integer는 Number의 하위타입이므로 지정 가능!
//		GenericClass3<String> gc2; // Number 계열이 아니므로 지정 불가!
//		GenericClass3<Object> gc3; // Number 의 상위 타입이므로 지정 불가!
		
	}

}

class Class1<P> {}

interface Interface1<Q> {}

// 부모 타입에 제네릭타입이 지정되 있을 경우
// 서브클래스에서 상속받을 때 부모의 타입 파라미터를 서브클래스 타입파라미터로 명시!
class SubClass<P, Q, R> extends Class1<P> implements Interface1<Q> {
	// => Class1<P>, Interface1<Q>를 상속받으로면 최소한 SubClass 뒤에 P와 Q를 명시 필수!
	//    또한, 서브클래스 자신만의 제네릭타입도 추가 가능!
	P var1; // 슈퍼클래스 Class1의 타입 P
	Q var2; // 슈퍼클래스 Interface1의 타입 Q
	R var3; // 자신의 타입 R
	
}

/*
 * 제네릭 타입에 대한 사용 가능한 파라미터 타입 제한
 * < 기본 문법 >
 * 1. 파라미터에 대한 서브클래스 타입으로 제한하는 경우
 *   class 클래스명<타입파라미터 extends 클래스타입> {}
 *   => 타입파라미터(E 또는 T 등)는 extends 뒤의 클래스타입이거나 하위 타입만 지정 가능
 * 
 */

//class GenericClass3<E> {} // 타입 파라미터 E는 어떤 타입으로 변경 가능

class GenericClass3<E extends Number> {}
// => Number 타입 또는 Number 클래스 하위 타입(Integer, Double 등)으로만 변환 가능































0개의 댓글