day25_1 - JAVA (자바, Generic)
[제네릭 타입(Generic)]
-> '컴파일 단계'에서 '잘못된 타입이 사용될 수 있는 문제'를 제거 가능
-> 컬렉션, 람다식(함수적 인터페이스), 스트림, NIO에서 널리 사용
-> 제네릭을 모르면 API 도큐먼트 해석이 어려우므로 학습이 필요하다.
-> 일반적인 코드를 작성하고, 이 코드를 다양한 타입의 객체에 대하여 재사용하는 프로그래밍 기법이다.
-> 클래스에서 사용할 타입을 클래스 외부에서 설정하는 타입이다.
[제네릭을 사용하는 코드의 이점]
-> 컴파일 시 강한 타입 체크가 가능
-> 컴파일 시에 미리 타입을 강하게 체크해서 에러를 사전에 방지한다.
[ex]
package javaPro.java_generic;
class MyClass01{
@Override
public String toString() {
return "MyClass01 문장 실행";
}
}
class Pool01 {
private Object object;
public void set(Object object) {
this.object = object;
}
public Object get(){
return object;
}
}
public class GenericEx01 {
public static void main(String[] args) {
Pool01 gen = new Pool01();
gen.set("그랜저"); // Object <------- String type
String name = (String)gen.get();
// get() 으로 받아오는 타입이 Object 이므로 name에 저장할때 (String)으로 형변환 필요
System.out.println(name);
gen.set(new MyClass01()); // Object <---------- MyClass type
MyClass01 g = (MyClass01)gen.get();
// get() 으로 받아오느 타입은 이역시 object 이므로 g에 저장할 때 (MyClass01)타입으로 형변한 필요
//String g2 (String) gen.get(); //이 문장은 error
System.out.println(g); // g에 저장된 MyClass01 메서드 실행
}
}
[제네릭 타입]
-> 타입을 파라미터로 가지는 클래스와 인터페이스
=> 선언 시 클래스 또는 인터페이스 이름 뒤에 '<>' 부호를 붙인다.
-> '<>' 사이에는 타입 파라미터 위치
타입 파라미터
-> 일반적으로 대문자 알파벳 한 문자로 표현
-> 개발 코드에서는 타입 파라미터 자리에 구체적인 타입을 지정해야 한다.
[제네릭 타입을 사용하지 않은 경우]
-> Object 타입 사용 -> 빈번한 타입 변환 발생 -> 프로그램 성능 저하
-> 제네릭은 타입이 Box 오브젝트로 들어가기 전부터 미리 타입을 사용자가 지정해서 집어넣는 기능이다.
[제네릭 타입을 사용한 경우]
-> 클래스를 선언할 때 타입 파라미터를 사용
-> 컴파일 시 타입 파라미터가 구체적인 클래스로 변경된다.
package javaPro.java_generic;
/*
제네릭을 이용
타입을 정하지 않고 class를 구현하는 방법이다
class의 멤버, 메소드 파라메터, 리턴 타입, 인터페이스의 타입을 실행시에 적용 할 수 있다
실행 사용시 제네릭 생략가능함
*/
class Pool02<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get(){
return t;
}
}
public class GenericEx02 {
public static void main(String[] args) {
// 제네릭 클래스이기 때문에 <String>를 지정해줘야 error가 안난다.
Pool02<String> p = new Pool02<String>();
p.set("hello");
System.out.println(p.get());
String str = p.get(); //이전 GenericEx1 코드와 다르게 (String) 형변환이 필요 없다.
System.out.println(str);
Pool02<Integer> p2 = new Pool02<Integer>();
p2.set(1);
System.out.println(p2.get());
int value = p2.get();
System.out.println(value);
}
}
[제네릭 타입은 두 개 이상의 타입 파라미터를 사용 가능]
[ex]
package javaPro.java_generic;
/*
제네릭 : <T, M> 은 모든 문자가 가능 하다. 주로 영문 대문자 한글자를 쓴다
*제네릭 class 구현
*/
class Product<T, M> {
private T kind;
private M model;
public T getKind() {
return this.kind;
}
public void setKind(T kind) {
this.kind = kind;
}
public M getModel() {
return this.model;
}
public void setModel(M model) {
this.model = model;
}
public String toString() {
return "Product [kind =" + kind + ", model = " + model + "]";
}
}
class Car {
public String toString() {
return "Car";
}
}
class Tv {
public String toString() {
return "Tv";
}
}
public class GenericEx3 {
public static void main(String[] args) {
Product<Tv, String> product1 = new Product<Tv, String>();
product1.setKind(new Tv()); // 메소드를 매개변수로 받아 저장
product1.setModel("스마트Tv"); // String 을 매개변수로 받아 저장
Tv tv = product1.getKind(); // 리턴 값이 tv 메서드
String tvModel = product1.getModel(); // 리턴 값이 String
System.out.println("Tv 메서드 결과값: " + tv + "\n" + "tvModel 변수 저장값: " + tvModel);
System.out.println(product1);
Product<Car, String> product2 = new Product<Car, String>();
product2.setKind(new Car());
product2.setModel("디젤");
Car car = product2.getKind();
String carModel = product2.getModel();
System.out.println("Car 메서드 결과값: " + car + "\n" + "carModel 변수 저장값: " + carModel);
System.out.println(product2);
}
}
자바 7부터는 다이아몬드 연산자를 사용해 간단히 작성 가능
[제네릭 메소드]
-> 매개변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드
-> 제네릭 메소드 선언 방법
-> 리턴 타입 앞에 '<>' 기호를 추가하고 타입 파라미터를 기술
-> 타입 파라미터를 리턴 타입과 매개변수에 사용한다.
[ex]
package javaPro.java_generic;
/*
* 제네릭 Method구현 : class 이용 하지 않고 메소드만을 제네릭으로 구현한다
* 실행 사용시 제네릭 생략가능함
*
*
*/
class Pool04<T> {
private T t;
public void set(T t) {
this.t = t;
}
public T get() {
return t;
}
}
// 다양 한 방식의 메소드들을 Generic으로 구현한 코드
class Util04 {
public static <T> Pool04<T> staticMethod(T t) {
Pool04<T> box = new Pool04<T>();
box.set(t);
return box;
}
public <T> Pool04<T> instanceMethod1(T t) {
Pool04<T> box = new Pool04<T>();
box.set(t);
return box;
}
public <T> String instanceMethod2(T t) {
return t.toString();
}
}
class MyCar {
public String toString() {
return "MyCar";
}
}
public class GenericEx4 {
public static void main(String[] args) {
Pool04<Integer> box1 = Util04.<Integer>staticMethod(100); // 제네릭 생략 가능
int intValue = box1.get();
System.out.println(intValue);
Util04 u = new Util04(); // 제네릭을 생략했기 때문에 어떤 값이든 가능
Pool04<String> box2 = u.<String>instanceMethod1("홍길동"); // 생략 가능
String strValue = box2.get();
System.out.println(strValue);
System.out.println(u.instanceMethod2(100)); // 정수 문자 메소드 다 잘 들어간다.
System.out.println(u.instanceMethod2("김자바"));
System.out.println(u.instanceMethod2(new MyCar()));
}
}
[제네릭 타입 제한하기]
package javaPro.java_generic;
class Pool05<T extends Number> { // Number를 상속받은 타입으로만 제한하겠다는 의미
T v;
Pool05(T n){
v = n;
}
void set(T n) {
v = n;
}
T get() {
return v;
}
}
class Util05 {
public static <T extends Number> int compare(T t1, T t2) {
double v1 = t1.doubleValue();
// System.out.println(t1.getClass().getName());
double v2 = t2.doubleValue();
// System.out.println(t2.getClass().getName());
return Double.compare(v1, v2); // compare: 앞이 크면 1 출력 , 뒤가 크면 -1 출력
}
}
public class GenericEx5 {
public static void main(String[] args) {
// 제네릭 클래스는 <> 생략이 가능하다.
//T는 Number를 상속받는 타입으로만 제한했기 때문에 String 타입은 error
//String str = Util05.compare("a", "b");
int result1 = Util05.<Integer>compare(10, 20);
System.out.println(result1); //1 출력
int result2 = Util05.compare(4.5, 3);
System.out.println(result2); //-1 출력
Pool05<Integer> p1 = new Pool05<Integer>(20);
Pool05<Double> p2 = new Pool05<Double>(20.0);
//제네릭 에서 T는 Number를 상속받는 타입으로만 제한했기 때문에 String 타입과 Character은 error
//Pool05<String> p3 = new Pool05<String>("hello");
//Pool05<Character> p4 = new Pool05<Character>('a');
Pool05<Byte> p5 = new Pool05<Byte>((byte) 1);
int value = p1.get();
System.out.println(value);
System.out.println(p2.get());
System.out.println(p5.get());
}
}
[제네릭 타입 배열 입력]
package javaPro.java_generic;
//제네릭 타입 배열 입력하기 구현
class Generic1<T> {
T[] v;
public T[] getV() {
return this.v;
}
public void setV(T[] v) {
this.v = v;
}
public void print() {
for (T s : v)
System.out.print(s + ",");
System.out.println();
}
}
public class GenericEx7 {
public static void main(String[] args) {
Generic1<String> t = new Generic1<String>();
String[] ss = { " 월매 ", " 순향 ", " 향단 " };
t.setV(ss);
t.print();
for (String s : t.getV())
System.out.println(s.trim());
Integer[] ii = { 10, 20, 30 };
// t.set(ii); // 지금 t는 String 타입이기 때문에 Integer 넣으면 오류
Generic1<Integer> t2 = new Generic1<Integer>();
t2.setV(ii);
t2.print();
for (Integer s : t2.getV())
System.out.println(s / 10);
}
}