제네릭(generic)

Mia Lee·2022년 1월 14일
0

JAVA

목록 보기
93/98
package ex_generic;

import java.util.*;

public class Ex1 {

	public static void main(String[] args) {

		/*
		 * 제네릭(Generic, 일반화)
		 * 
		 * < 제네릭 타입 지정을 통한 객체 생성 방법 >
		 * 클래스명<제네릭타입명> 변수명 = new 클래스명<제네릭타입명>();
		 * 
		 */
		
		// Collection Framework(ArrayList 등)의 객체들은 파라미터로 Object 타입을 주로 사용
		ArrayList list = new ArrayList();
		// Object 타입 파라미터를 갖는 add() 메서드는 어떤 데이터타입도 모두 추가 가능하므로
		// 데이터를 추가하는 시점에서는 매우 편리함.
		list.add(1);
		list.add("TWO");
		list.add(3.14);
		
		// 반복문을 사용하여 List 객체의 모든 요소 꺼내서 변수에 저장 후 출력
		for (int i = 0; i < list.size(); i++) {
//			int data = list.get(i); // 오류 발생!(Object 타입 리턴 시 int 타입에 저장 불가)
//			int data = (int) list.get(i); // 오류 발생!
			// => 첫번째 데이터는 정수이므로 int형으로 변환 가능하지만,
			//    두번째 데이터는 문자열이므로 int형으로 변환이 불가능함
			
			// 모든 데이터타입을 구분없이 저장하기 위해서는 Object 타입 변수 필요함
			Object data = list.get(i); // 형변환 불필요
//			System.out.println(data);
			// => 하나의 변수에 모든 타입을 저장 가능한 장점이 있지만
			//    저장된 데이터를 활요하여 작업을 수행하는 경우
			//    각 타입이 달라서 문제가 발생할 가능성이 있다!
			
			// 만약, 어떤 문제도 발생하지 않도록 각 데이터타입에 맞는 변수에 저장하려면
			// 데이터를 꺼낼 때 타입 판별을 통해 알맞은 타입으로 저장해야한다!
			// => instanceof 연산자를 사용하여 각 타입 판별 후 변수에 저장
			//    각 타입으로 저장할 때 Object -> 각 타입으로 형변환 필수!
			if (data instanceof Integer) { // 정수 판별
				int iData = (int) data;
				System.out.println(iData);
				
			} else if (data instanceof String) { // 문자열 판별
				String strData = (String) data;
				System.out.println(strData);
				
			} else if (data instanceof Double) { // 실수 판별
				double dData = (double) data;
				System.out.println(dData);
				
			}
			// => Object 타입을 사용하여 데이터를 저장하는 경우
			//    저장 시점에서는 데이터 타입 구분이 필요없으므로 편리하지만
			//    실제 데이터를 사용하는 시점에서는 데이터타입이 달라서 문제가 발생할 수 있음
			// => 또한, 각 데이터타입 변수에 저장할 때는 추가적으로 형변환이 필요함!
			
		}
		
		System.out.println("================================================");
		
		// 제네릭 타입을 지정하여 ArrayList 객체들을 사용!
		// => 객체 생성 시점에서 저장될(사용될) 데이터타입을 지정하면
		//    해당 데이터타입만 사용 가능하도록 변경됨
		// => 클래스명 뒤에 <> 기호를 명시하고, 기호 사이에 사용할 데이터타입 지정
		//    데이터타입은 반드시 참조형 데이터타입만 지정 가능!(기본형 사용 불가)
		//    ex) int(x) -> Integer, char(x) -> Character
		
		// 제네릭 타입으로 정수를 사용하기 위해 Integer 타입을 지정
		ArrayList<Integer> list2 = new ArrayList<Integer>();
		// 제네릭 타입으로 Integer 타입 지정 시
		// Object 타입을 사용하던 모든 메서드가 Integer 타입으로 변화하게 됨
		// 따라서, add() 메서드의 파라미터도 Integer 타입이므로
		// 정수 타입 외의 나머지 데이터타입 추가 시 오류 발생하게 된다!
		list2.add(1);
//			list2.add("TWO"); // 컴파일 에러 발생! Integer 타입만 추가 가능
//			list2.add(3.14); // 컴파일 에러 발생!
		list2.add(2);
		list2.add(3);
		// => 데이터를 추가하는 시점(컴파일 시점)이세ㅓ 오류가 발견 가능하며
		//    데이터타입 감지를 통해 오류 발생 가능성을 최소화 시킬 수 있다!
		
		for (int i = 0; i < list2.size(); i++) {
			// 반복문 등을 통해 데이터를 꺼낼 때
			// 제네릭 타입으로 리턴되므로 별도의 형변환이나 데이터타입 검사가 불필요함
			int data = list2.get(i);
			System.out.println(data);
			// => instanceof 불필요, 형변환 연산자 불필요
			// => 특정 데이터타입만 저장된다는 보장이 생기므로 간편하게 사용 가능!
			
		}
		
		// 향상된 for문 사용 시
		for (int data : list2) {
			System.out.println(data);
			
		}
		
		System.out.println("==============================");
		
		// ArrayList 객체(list3) 생성 => 제네릭 타입을 String으로 지정
		ArrayList<String> list3 = new ArrayList<String>();
		// => list3 객체의 대부분이 String 타입 파라미터 또는 리턴타입이 String으로 변경됨
		
		System.out.println(list3.add("홍길동"));
//		list3.add(1); // 컴파일 에러 발생!
		
		for (String data : list3) {
			System.out.println(data);
			
		}
		
	}

}







package ex_generic;

import java.util.*;

public class Test1 {

	public static void main(String[] args) {

		// Person 클래스 인스턴스 2개 생성
		// "홍길동", 20
		// "이순신", 44
		Person p1 = new Person("홍길동", 20);
		Person p2 = new Person("이순신", 44);
		
		
		System.out.println(p1);
		System.out.println(p2.toString());
		
		// Person 객체 여러개를 하나의 객체에 저장하여 관리할 경우
		// 1. Object[] 배열을 통해 관리
		// => 배열은 크기가 불변이므로 추가적인 객체 저장 불가
		Object[] objectArr = {p1, p2}; // Person -> Object 업캐스팅 되어 관리됨
		
		for (int i = 0; i < objectArr.length; i++) {
			// Object[] 배열 내의 Person 객체를 Object 타입 변수에 저장하면
			// 업캐스팅 된 상태이므로 서브클래스인 Person의 상세정보가 보이지 않는다!
//			Object o = objectArr[i];
//			System.out.println(o.getName()); // 컴파일에러 발생! 참조영역 축소로 인해서 접근 불가!
			
			// Object[] 배열 내의 Person 객체를 꺼내서 Person 타입 변수에 저장
//			Person p = objectArr[i]; // Object -> Person 다운캐스팅 필수!
			Person p = (Person) objectArr[i]; // 다운캐스팅
			
			// Getter 메서드를 호출하여 이름, 나이를 출력
			System.out.println("이름 : " + p.getName());
			System.out.println("나이 : " + p.getAge());
			
		}
		
		// 2. ArrayList(Collection)를 활용하여 Person 객체 여러개를 관리할 경우
		// => 배열의 단점인 크기 불변을 해결하게 되므로 여러 객체를 자유롭게 추가 가능
		// 1) 제네릭을 사용하지 않을 경우
		//    - 파라미터 또는 리턴타입 Object 타입이 되어 다양한 객체 저장 가능
		//    - 저장 시점에서 타입 판별이 이루어지지 않으므로 편리하지만
		//      대신 데이터를 꺼내는 시점에서 타입 불일치에 따른 오류 발생 가능성이 존재!
		//    - 데이터를 꺼내기 전 instanceof 연산자를 통한 타입 판별을 수행해야하며
		//      Objcet 타입을 실제 데이터타입으로 다운캐스팅을 수행!
		ArrayList list = new ArrayList();
		
		list.add(p1);
		list.add(p2);
		list.add(new Person("강감찬", 35)); // 새로 추가되는 객체도 자동으로 확장되어 저장
		
		list.add("전지현"); // Person 객체가 아닌 데이터도 추가가 가능함
		// => Person 객체 형태로 꺼내서 사용하는 시점에서 문제가 발생할 수도 있다!
		
		// for문을 사용하여 ArrayList 객체 반복
		// => Person 타입 변수에 저장 후 Getter 메서드 호출
		for (int i = 0; i < list.size(); i++) {
			// 잘못된 데이터가 저장되어 있을 수도 있으므로 타입 판별 후 형변환 필요함
			if (list.get(i) instanceof Person) { // list.get(i) 가 Person 타입인가?
//				System.out.println(list.get(i));
//				System.out.println(list.get(i).getName()); // 오류 발생!
				
//				Person p = list.get(i); // 리턴타입이 Object 이므로 Person 타입에 저장 불가!
				Person p = (Person) list.get(i); // 다운캐스팅 필수!
//				System.out.println(p);
				System.out.println("이름 : " + p.getName());
				
			} else {
				System.out.println("타입이 일치하지 않습니다!");
				
			}
			
		}
		
		System.out.println("===========================================");
		
		// 2) 제네릭을 사용할 경우
		//    => 저장할 객체 타입이 Person 타입이므로 제네릭 타입 <Person> 지정
		//    - 객체 저장 시 Person 타입 객체만 저장 가능하도록 자동으로 판별 수행
		//      즉, 잘못된 객체가 저장될 우려가 없다!
		//    - Object 타입으로 업캐스팅 되지 않고 Person 타입 자체로 저장되기 때문에
		//      객체를 꺼내는 시점에서도 Person 타입 그대로 꺼낼 수 있다!
		//      => 별도의 다운캐스팅 등 형변환이 불필요!
		//    - 또한, 추가적인 타입 판별도 불필요!
		ArrayList<Person> list2 = new ArrayList<Person>();
		// 제네릭타입 <Person> 지정 시 사용 가능한 객체는 무조건 Person 타입 객체만 사용 가능!
		list2.add(p1);
		list2.add(p2);
		list2.add(new Person("강감찬", 35)); // 새로 추가되는 객체도 자동으로 확장되어 저장
		
		// Person 타입이 아닌 객체를 추가 할 경우
		// 데이터 타입 판별에 의해 오류가 발생하게 된다!
//		list2.add("전지현"); // 컴파일 에러 발생!
//		list2.add(new Object());
		
		// 반복문을 통해 list2 인스턴스의 모든 요소 꺼내서 출력
		for (int i = 0; i < list2.size(); i++) {
			// Person 타입 변수에 저장해서 출력
			Person p = list2.get(i); // 형변환 불필요!(리턴타입이 Person)
			System.out.println("이름 : " + p.getName());
			
		}
		
	}

}

/*
 * Person 클래스 정의
 * - 멤버변수 : 이름(name, 문자열), 나이(age, 정수) => private
 * - 생성자 : 이름과 나이를 전달받아 초기화하는 생성자
 * - Getter / Setter 정의
 * - toString() 메서드 오버라이딩
 *   => ex) 이름, 나이
 */
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;
	}
	
}



















0개의 댓글