Java

[이것이 자바다] 자바 제너릭 공부 정리

Razelo 2020. 12. 21. 17:02

none_generic_type

package sec02_exam01_none_generic_type;

public class Apple {
	
}
package sec02_exam01_none_generic_type;

public class Box {
	private Object object;
	
	public void set(Object object) {
		this.object = object;
	}
	public Object get() {
		return object;
	}
}
package sec02_exam01_none_generic_type;

public class BoxExample {

	public static void main(String[] args) {
		Box box = new Box();
		box.set("홍길동");

		String name = (String) box.get();
		
		box.set(new Apple());
		Apple apple = (Apple)box.get();
	}

}

 

 

 

generic_type

package sec02.exam02_generic_type;

public class Box <T>{
	private T t;
	
	public void set(T t) {
		this.t = t;
	}
	public T get() {
		return t;
	}
}
package sec02.exam02_generic_type;

public class BoxExample {
	//구체적인 타입을 지정하지 않으면, Object로 들어간다. 그래서 타입 지정안하면 경고표시뜨는거임. 
	public static void main(String[] args) {
		//제너릭 타입을 사용하는 것이 프로그램의 성능에 좋은 결과를 가져다준다. 
		Box<String> box1 = new Box<String>();
		box1.set("hello");
		String greet = box1.get();
		
		Box<Integer> box2 = new Box<Integer>();
		box2.set(6);
		int value = box2.get();

	}

}

 

 

 

multi_type_parameter

package sex03.exam01_multi_type_parameter;

public class Car {

}
package sex03.exam01_multi_type_parameter;
// alt + shift + s 누르면 자동으로 게터 세터 메소드 생성할 수 있음. 
public class Product<T, M> {
	private T kind;
	private M model;
	
	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;
	}

}
package sex03.exam01_multi_type_parameter;

public class Tv {

}
package sex03.exam01_multi_type_parameter;

public class ProductExample {

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

}

 

 

 

  generic_method1

package sec04.exam01_generic_method;

public class Box <T>{
	private T t;
	
	public void set(T t) {
		this.t = t;
	}
	public T get() {
		return t;
	}
}
package sec04.exam01_generic_method;

public class Util {
	//제너릭 메소드. T를 매개변수 타입과 리턴타입에서 사용하겠다는 뜻이다. 
	public static <T> Box<T> boxing(T t){
		Box<T> box = new Box<>();
		box.set(t);
		return box;
	}
}
package sec04.exam01_generic_method;

public class BoxingMethodExample {

	public static void main(String[] args) {
		//정적 메소드여서 Util. 으로 접근할 수 있다. 
		Box<Integer> box1 = Util.<Integer>boxing(100);
		int intValue = box1.get();
		System.out.println(intValue);
		
		Box<String> box2 = Util.boxing("홍길동"); //컴파일러는 유추를 한다. <String>이 있는걸로 해석을 한다. 
		String strValue = box2.get();  
		System.out.println(strValue);
	}

}

 

 

 

generic_method2

package sec04.exam02_generic_method;

public class Pare<K,V> {
	private K key;
	private V value;
	
	public Pare(K key, V value) {
		this.key = key;
		this.value = value;
	}
	
	public K getKey() {
		return key;
	}
	public void setKey(K key) {
		this.key = key;
	}
	public V getValue() {
		return value;
	}
	public void setValue(V value) {
		this.value = value;
	}
	
	
}
package sec04.exam02_generic_method;

public class Util {
	public static <K, V> boolean compare(Pare<K, V> p1, Pare<K, V> p2) {
		boolean keyCompare = p1.getKey().equals(p2.getKey());
		boolean valueCompare = p1.getValue().equals(p2.getValue());
		return keyCompare && valueCompare;
	}

}
package sec04.exam02_generic_method;

public class CompareMethodExample {

	public static void main(String[] args) {
		Pare<Integer,String> p1 = new Pare<>(1,"사과");
		Pare<Integer,String> p2 = new Pare<>(1,"사과");
		//Util.<Integer,String>compare(p1, p2);
		boolean result1 = Util.<Integer,String>compare(p1, p2); //컴파일러가 유추할 수 있기 때문에 <> 없어도 됨. 
		if(result1) {
			System.out.println("논리적으로 동등한 객체입니다.");
		}else {
			System.out.println("논리적으로 동등하지 않는 객체입니다.");
		}
		
		Pare<String,String> p3 = new Pare<>("user1","홍길동");
		Pare<String,String> p4 = new Pare<>("user2","홍길동");
		boolean result2 = Util.compare(p3, p4);  
		if(result2) {
			System.out.println("논리적으로 동등한 객체입니다.");
		}else {
			System.out.println("논리적으로 동등하지 않는 객체입니다.");
		}
		
	}

}

 

 

 

bounded_type

package sec05.exam01_bounded_type;

public class Util {
	// 정적 메소드 .-> 이름은 compare 타입 파라미터로 매개변수 2개를 선언한다. -> 그래서 리턴타입 앞에 타입파라메터선언부분이 있어야 한다. <T extends ... >
	//T만 온게 아니라 extends Number가 있다. java.lang 의 Number는 추상 클래스이다. 
	public static <T extends Number> int compare(T t1, T t2) { // 꼭 넘버타입으로만 사용해라... 라는 뜻! 
		double v1 = t1.doubleValue();
		double v2 = t2.doubleValue();
		return Double.compare(v1, v2); //v1이 작으면 -1 크면 1 같으면 0 
	}
}
package sec05.exam01_bounded_type;

public class BoundedTypeParameterExample {

	public static void main(String[] args) {
		int result1 = Util.<Integer>compare(10, 20); //Integer 생략 가능함. 
		System.out.println(result1);
		int result2 = Util.compare(4.5, 3);
		System.out.println(result2);
	}

}

 

 

 

generic_wild_card

package sec06.exam01_generic_wildcard;

public class Course<T> { // 과정 클래스 어떤 종류의 학생이 올지 모르니 제너릭 타입이다. 
	private String name;
	private T[] students;
	
	public Course(String name,int capacity) {
		this.name = name;
		//아직 T가 결정되지 않은상태여서 배열만드는 일반적인 방식으로는 못만든다.
		//그러므로 일단 오브젝트 타입으로 만들고 난 뒤에T타입으로 바꿔야 한다.
		students = (T[])(new Object[capacity]); 
	}
	
	public String getName() {return name;}
	public T[] getStudents() {return students;}
	
	public void add(T t) {
		for(int i = 0;i<students.length;i++) {
			if(students[i] == null) { //비어있는 곳을 찾아서 새로운 값을 넣어준다. 
				students[i] = t;
				break;
			}
		}
	}
}
package sec06.exam01_generic_wildcard;

public class Person { //일반인
	private String name;
	public Person(String name) {
		this.name = name;
	}
	
	public String getName() {return name;}
	@Override
	public String toString() {
		return name;
	}
}
package sec06.exam01_generic_wildcard;

public class Student extends Person{ //학생 클래스 

	public Student(String name) {
		super(name);
	}
	
}
package sec06.exam01_generic_wildcard;

public class HighStudent extends Student {

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

}
package sec06.exam01_generic_wildcard;

public class Worker extends Person{ //학생 클래스 

	public Worker(String name) {
		super(name);
	}
	
}
package sec06.exam01_generic_wildcard;

import java.util.Arrays;

public class WildCardExample {
	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()));
	}
	
	public static void main(String[] args) {
		
		Course<Person> personCourse = new Course<>("일반인 과정",5);
		personCourse.add(new Person("일반인"));
		personCourse.add(new Person("직장인"));
		personCourse.add(new Person("학생"));
		personCourse.add(new Person("고등학생"));
		
		Course<Worker> workerCourse = new Course<>("직장인 과정",5);
		workerCourse.add(new Worker("직장인"));
		
		Course<Student> studentCourse = new Course<>("학생 과정",5);
		studentCourse.add(new Student("학생"));
		studentCourse.add(new HighStudent("고등학생"));
		
		Course<HighStudent> highStudentCourse = new Course<>("고등학생 과정",5); 
		highStudentCourse.add(new HighStudent("고등학생"));
		
		registerCourse(personCourse);
		registerCourse(workerCourse);
		registerCourse(studentCourse);
		registerCourse(highStudentCourse);
		System.out.println();

		//registerCourseStudent(personCourse);
		//registerCourseStudent(workerCourse);
		registerCourseStudent(studentCourse);
		registerCourseStudent(highStudentCourse);
		System.out.println();

		registerCourseWorker(personCourse);
		registerCourseWorker(workerCourse);
		//registerCourseWorker(studentCourse);
		//registerCourseWorker(highStudentCourse);
		System.out.println();
	}

}

 

 

 

generic_extends_implements

package sec07.exam01_generic_extends_implements;

public class Product<T,M> {	
	private T kind;
	private M model;
	
	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;
	}
}

class Tv{}
package sec07.exam01_generic_extends_implements;

public class ChildProduct<K,V,C> extends Product<K,V>{ //부모가 제너릭이면 자식도 제너릭 
	private C company;

	public C getCompany() {
		return company;
	}

	public void setCompany(C company) {
		this.company = company;
	}
	
}
package sec07.exam01_generic_extends_implements;

public interface Storage<T> { //인터페이스는 객체 사용설명서와 같다. 
	public void add(T item ,int index); //인덱스 위치에서 추가한다. 
	public T get(int index); //인덱스 위치에서 찾는다.
}
package sec07.exam01_generic_extends_implements;

//인터페이스가 제너릭이면, 구현 클래스도 제너릭이다. 
public class StorageImpl<T> implements Storage<T>{
	private T[] array;
	
	public StorageImpl(int capacity) {
		array = (T[])(new Object[capacity]);
	}
	@Override
	public void add(T item, int index) {
		array[index] = item;
	}

	@Override
	public T get(int index) {
		return array[index];
	}
	

}
package sec07.exam01_generic_extends_implements;

public class ChildProductAndStorageExample {

	public static void main(String[] args) {
		ChildProduct<Tv,String,String> product = new ChildProduct<>();
		product.setKind(new Tv());
		product.setModel("SmartTV");
		product.setCompany("Samsung");
		
		Storage<Tv> storage = new StorageImpl<Tv>(100);
		storage.add(new Tv(), 0);
		Tv tv = storage.get(0);
	}

}

 

 

 

필기 내용 

package generic;
/*
 <제너릭 타입이란. >
 타입을 파라미터로 가지는 클래스와 인터페이스를 말한다. 
 
<제너릭>
클래스를 선언할 떄 타입 파라미터 기술
컴파일 시 타입 파라미터가 구체적인 클래스로 변경됨. 
  
 <비제네릭>
 Object 타입을 사용함으로써 빈번한 타입 변한 발생 -> 성능 저하 
 
 
 멀티 타입 파라미터
 두개 이상의 타입 파라미터를 사용해서 선언할 수 있다. 
 class<K,V,...>{...}
 
 자바 7부터는 중복된 타입 파라미터를 생략해도 된다. 
 <> 안에 들어가는거 중복으로 안해도 된다는 뜻. 
 

 <제너릭 메소드 >
 매개변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드를 말한다. 
 리턴 타입 앞에 <> 기호를 추가하고 타입 파라미터를 기술한다. 
 타입 파라미터를 리턴타입과 매개변수에 사용한다. 
 public <T> Box<T> boxing(T t){...}
 
 
<타입 파라미터에 지정되는 구체적인 타입을 제한할 필요가 있을 경우 >
상속 및 구현 관계를 이용해서 타입을 제한한다. 
public <T extends 상위타입> 리턴타입 메소드(매개변수,...){...}
상위 타입은 클래스 뿐만 아니라 인터페이스도 가능하다. -> 인터페이스라고 해서 extends 대신 implements를 사용하지 않는다. 

<타입 파라미터를 대체할 구체적인 타입>
상위 타입이거나 하위 또는 구현 클래스만 지정할 수 있다. 

<주의할점>
메소드의 중괄호 {}안에서 타입 파라미터 변수로 사용 가능한 것은 상위 타입의 멤버(필드,메소드)로 제한된다.
하위 타입에만 있는 필드와 메소드는 사용할 수 없다. 

 
  와일드카드(?) 타입 
 제너릭 타입을 매개변수나 리턴타입으로 사용할 때 타입 파라미터를 제한할 목적
 
 <와일드카드 타입의 세가지 형태>
 1.제너릭타입<?>: Unbounded Wildcards (제한 없음)
 타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다. 
 
 2.제너릭타입<? extends 상위타입> : Upper Bounded Wildcards (상위 클래스 제한)
 타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 하위 타입만 올 수 있다.
 
 2.제너릭타입<? super 하위타입> : Lower Bounded Wildcards (하위 클래스 제한)
 타입 파라미터를 대치하는 구체적인 타입으로 하위 탕딥이나 상위 타입이 올 수 있따. 
  
 
 
 <제너릭 타입의 상속과 구현 >
 제너릭 타입을 부모 클래스로 사용할 경우 -> 
 타입 파라미터는 자식 클래스에도 기술해야 한다. 
 public class ChildProduct<T,M> extends Product<T,M> {...}
 
 추가적인 타입 파라미터를 가질 수 있다. 
 public class ChildProduct<T,M,C> extends Product<T,M> {...}
 
 제너릭 인터페이스를 구현할 경우 -> 
 타입 파라미터는 구현 클래스에도 기술해야 한다. 
 public class StorageImpl <T> implements Storage<T> {...}
  
  
 */

 

 

반응형