[Java]Generic

박진우·2022년 9월 16일
0

[Java]자바의 기초

목록 보기
15/16

제네릭(Generic) 타입이란?

‘컴파일 단계’에서 ‘잘못된 타입 사용될 수 있는 문제’제거 가능
자바5부터 새로 추가 !
컬렉션, 람다식(함수적 인터페이스), 스트림, NIO에서 널리 사용
제네릭을 모르면 API 도큐먼트 해석 어려우므로 학습 필요

제네릭을 사용하는 코드의 이점

  • 컴파일 시 강핚 타입 체크 가능
    • 실행 시 타입 에러가 나는 것 방지
    • 컴파일 시에 미리 타입을 강하게 체크해서 에러 사젂 방지
    타입변환 제거 가능

제네릭 타입이란?

타입을 파라미터로 가지는 클래스와 인터페이스
선언 시 클래스 또는 인터페이스 이름 뒤에 ‚<>‛ 부호 붙임 ‚<>‛ 사이에는 타입 파라미터 위치

  • 타입 파라미터
    • 일반적으로 대문자 알파벳 핚 문자로 표현
    • 개발 코드에서는 타입 파라미터 자리에 구체적인 타입을 지정해야

Generic(box)

// <T> : generic data type  (<-> specific)
// 1. class이름 바로 다음에 사용
// 2. T는 class Box에서 사용하는 data type임 (가상의 데이터 타입, 아직 결정되지 않은 데이터 타입)
// 3. T 대신에 어떠한 다른 글자를 사용해도 됨 (T : template)
// 4. 통상 1글자의 영문자를 사용
// 5. Box<T> : data type이면서 class 이름 으로 간주 가능
public class Box<T> {
	private T t;

	public T getT() {
		return t;
	}

	public void setT(T t) {
		this.t = t;
	}
		
}

Main

// Box<String> : data type (T가 String이라는 의미임)
// => JVM이 실행할 때 Box<String>에 대한 class를 만들어 놓음
// => Box<Apple>, Box<Integer>도 JVM이 실행할 때 별도로 class를 만들어 놓음
// 단점 : class가 메모리에 여러개 만들어 짐
// 1. compile할 때 data type이 결정되어, 어떠한 data type을 사용하더라도 casting 없이 사용 가능
// 2. Generic type은 class뿐만 아니라, Interface에서도 사용 가능, method에 대해서도 사용 가능
public class BoxEx {

	public static void main(String[] args) {
		Box<String> b1 = new Box<String>();
		b1.setT("hello");
		String str = b1.getT();
		
		Box<Apple> a1 = new Box<Apple>();
		a1.setT(new Apple());
		Apple apple = a1.getT();
		
		Box<Integer> i1 = new Box<Integer>();
		i1.setT(new Integer(10) );
		Integer ii = i1.getT();	

	}

}

멀티 타입 파라미터

Generic

Generic type으로 T, M을 사용하겠다는 의미 (generic multi type)

  • generic type뿐만 아니라, 일반 primitive type, 다른 class들도 사용 가능
  • 대부분의 class들은 최대 3개까지의 generic type 사용

public class Product<T, M> {
	private T kind;
	private M model;
	private int age;
	
	public T getKind() {
		return kind;
	}
	public void setKind(T kind) {
		this.kind = kind;
	}
	
	public M getModel() {
		return model;
	}
	public void setModel(M model) {
		this.model = model;
	}

}

Main

class Tv {}
class Car {}


public class ProductEx {

	public static void main(String[] args) {
		Product<Tv, String> p1 = new Product<Tv, String>();
		p1.setKind(new Tv());
		p1.setModel("스마트TV");
		Tv tv = p1.getKind();
		String tvModel = p1.getModel();
		
		Product<Car, String> p2 = new Product<Car, String>();
		p2.setKind(new Car());
		p2.setModel("디젤자동차");
		Car car = p2.getKind();
		String carModel = p2.getModel();
	}

}

Generic method

매개변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드
제네릭 메소드 선언 방법
• 리턴 타입 앞에 ‚<>‛ 기호를 추가하고 타입 파라미터 기술
• 타입 파라미터를 리턴 타입과 매개변수에 사용

Generic(box)

public class Box<T> {
	private T t;

	public T getT() {
		return t;
	}

	public void setT(T t) {
		this.t = t;
	}
		
}

Generic method (Util)

//<T> Box<T> boxing(T t) 
//1. 처음에 나타난 <T> 의미 : method 안에서 T를 data type으로 사용
//2. Box<T> : method의 return type
  
public class Util {

	public static <T> Box<T> boxing(T t) {
		Box<T> box = new Box<T>();
		box.setT(t);
		
		return box;
	}
}

Main

public class GenericMethodEx {

	public static void main(String[] args) {
		Box<Integer> b1 = Util.<Integer>boxing(100);	// auto boxing (primitive -> Integer)
		
		int i1 = b1.getT();
		
		System.out.println(i1);
		
		Box<String> s1 = Util.<String>boxing("홍길동");
		String name = s1.getT();
		
		System.out.println(name);

	}

}

GenericExtends

// <T extends Number> : 
// 1. T는 부모인 Number나 자식들인 Byte, Double, Float, Integer, Long, Short class만 가능하다는 의미
// 2. primitive type의 wrapper class들만 사용 가능
// Double.compare(a, b) method의 return 값
// 1) a < b이면, -1 return
// 2) a == b이면, 0 return
// 3) a > b이면, 1 return
public class Util {

	public static <T extends Number> int compare(T t1, T t2) {
		double d1 = t1.doubleValue();
		double d2 = t2.doubleValue();
		
		return Double.compare(d1, d2);
	}
}

Main

public class GenericExtendsEx {

	public static void main(String[] args) {
//		int result = Util.compare("a", "b");

		int result = Util.compare(10, 20);
		System.out.println(result);
		
		result = Util.compare(4.5, 3);
		System.out.println(result);
	}

}

제네릭 타입의 상속과 구현

Class(Person,Student,Worker)

public class Person {
	String name;

	public Person(String name) {
		this.name = name;
	}

}


public class Student extends Person {

	public Student(String name) {
		super(name);
	}
}


public class Worker extends Person {

	public Worker(String name) {
		super(name);
	}
}

Generic(Course)

public class Course<T> {
	private String name;
	private T[] students;
	String a;
	
	public Course(String name, int capacity) {
		this.name = name;
		this.students = (T[]) new Object[capacity];	// new로 인스턴스 만들면 모든 참조타입의 field값은 null로 setting
		// this.students = new T[capacity];
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public T[] getStudents() {
		return students;
	}

	public void setStudents(T[] students) {
		this.students = students;
	}

	public void add(T t) {
		for (int i=0; i<students.length; i++) {
			if (students[i] == null) {
				students[i] = t;
				break;
			}
		}
	}
}

Main


// <?>, <? extends>, <? super > : method의 paramter나 return type에서만 사용 가능
public class RestrictedGenericEx {

	public static void main(String[] args) {
		Course<Person> personCourse = new Course<Person>("일반인과정", 5);
		System.out.println(personCourse.a);
		personCourse.add(new Person("일반인"));
		personCourse.add(new Worker("직장인"));
		personCourse.add(new Student("학생"));
		personCourse.add(new HighStudent("고등학생"));

		Course<Worker> workerCourse = new Course<Worker>("직장인과정", 5);

		workerCourse.add(new Worker("직장인"));

		
		Course<Student> studentCourse = new Course<Student>("학생과정", 5);

		studentCourse.add(new Student("학생"));
		studentCourse.add(new HighStudent("고등학생"));

		Course<HighStudent> highStudentCourse = new Course<HighStudent>("고등학생과정", 5);
		highStudentCourse.add(new HighStudent("고등학생"));
		
		registerCourse(personCourse);
		registerCourse(workerCourse);
		registerCourse(studentCourse);
		registerCourse(highStudentCourse);


		registerCourseStudent(studentCourse);
		registerCourseStudent(highStudentCourse);

		registerCourseWorker(personCourse);
		registerCourseWorker(workerCourse);

}

	public static void registerCourse(Course<?> course) {
		System.out.println(course.getName() + "수강생: " + Arrays.toString(course.getStudents()));
	}

	public static void registerCourseStudent(Course<? extends Student> course) {
		System.out.println(course.getName() + "수강생: " + Arrays.toString(course.getStudents()));
	}

	public static void registerCourseWorker(Course<? super Worker> course) {
		System.out.println(course.getName() + "수강생: " + Arrays.toString(course.getStudents()));
	}
}

강의를 듣고 느낀점

제네릭은 완전히 새롭게 배운거라 신기하면서도 뙈나 어려워서 강의가 끝나고 몇번 더 연습한 몇안되는 강의중 하나였다

profile
개발자를 꿈꾸는 사람입니다

0개의 댓글