-
java generic(1)대학/객체지향프로그래밍 2022. 12. 9. 23:03
하나의 코드를 다양한 타입의 객체에 재사용하는 객체 지향 기법으로
타입을 변수로 사용한다. (c++의 template와 유사)
단, 기초타입을 변수로 사용할 수 없다.
제네릭은 컴파일할 때 타입을 점검하기 때문에, type safety를 보장하여 실행 도중 발생할 오류를 방지한다.
또한 불필요한 타입 변환이 없기 때문에, 프로그램 성능에 도움을 준다. (타입 매개변수를 타입 인자로 대체할 뿐)
class Gen<T> { T ob; Gen(T o) { ob = o; } T getOb() { return ob; } void showType() { System.out.println("Type of T is " + ob.getClass().getName()); } } class GenDemo { public static void main(String[] args) { Gen<Integer> iOb; iOb = new Gen<Integer>(88); iOb.showType(); // Type of T is java.lang.Integer int v = iOb.getOb(); System.out.println("value: " + v); // value: 88 Gen<String> strOb = new Gen<String>("Generics Test"); strOb.showType(); // Type of T is java.lang.String String str = strOb.getOb(); System.out.println("value: " + str); // value: Generics Test } }
예시로 매개타입 변수를 하나만 썼지만, 두 개 이상을 사용해도 되고,
매개타입 변수의 타입이 같은걸 여러 개 사용해도 상관 없다.
Gen<Integer> iOb = new Gen<String>(“Generics Test”); // compile error
단, 타입 인자가 같아야만 호환된다.
class NonGen { Object ob; NonGen(Object o) { ob = o; } Object getOb() { return ob; } void showType() { System.out.println("Type of ob is " + ob.getClass().getName()); } } class NonGenDemo { public static void main(String[] args) { NonGen iOb = new NonGen(88); iOb.showType(); // java.lang.Integer int v = (Integer) iOb.getOb(); System.out.println("value: " + v); // 88 NonGen strOb = new NonGen("Non-Generics Test"); strOb.showType(); // java.lang.String String str = (String) strOb.getOb(); System.out.println("value: " + str); // Non-Generics Test iOb = strOb; // ok v = (Integer) iOb.getOb(); // runtime exception! } }
Object 사용시 호환은 된다. 하지만, 타입 캐스팅시 런타임 에러가 발생할 수 있다.
또한, java 런타임 시스템은 iob가 Integer라는 것을 알고있지만,
리턴 타입이 Object라 명시되어 있기 때문에 타입 캐스팅을 해줘야 한다.
- 타입 한정
class Stats<T extends Number> { T[] nums; Stats(T[] o) { nums = o; } double average() { double sum = 0.0; for(int i=0; i < nums.length; i++) sum += nums[i].doubleValue(); return sum / nums.length; } } class BoundsDemo { public static void main(String[] args) { Integer[] inums = { 1, 2, 3, 4, 5 }; Stats<Integer> iob = new Stats<Integer>(inums); double v = iob.average(); System.out.println("iob average is " + v); Double[] dnums = { 1.1, 2.2, 3.3, 4.4, 5.5 }; Stats<Double> dob = new Stats<Double>(dnums); double w = dob.average(); System.out.println("dob average is " + w); // This won't compile because String is not a subclass of Number. // String[] strs = { "1", "2", "3", "4", "5" }; // Stats<String> strob = new Stats<String>(strs); // double x = strob.average(); // System.out.println("strob average is " + v); } }
원래였다면 nums[i]는 T 타입이기 때문에 doubleValue메서드를 사용할 수 없었다.
하지만, Number라는 superclass를 상속했기 때문에 Number클래스에 정의된 doubleValue메서드를 사용할 수 있게 되었다.
단, Number와 Number의 subclass인 Integer, Float, Double만 타입 인자로 사용할 수 있다.
class GEN<T extends MyClass & MyInterface> { ... }
위와 같이 인터페이스 타입으로 한정지을 수도 있다.
위의 경우에는 class T extneds MyClass implements MyInterface 를 만족하는 클래스 T만 사용 가능하다.
class Stats<T extends Number> { ... boolean isSameAvg(Stats<?> ob) { // 와일드카드 인자 if(average() == ob.average()) return true; return false; } } ob1 = new Stats<Integer>; ob2 = new Stats<Double>; ob1.isSameAvg(ob2);
isSameAvg에 타입 매개변수를 T라고 하면 컴파일 에러가 발생한다.
이 때는 ?라는 와일드카드 인자를 사용하여 해결한다.
단, 와일드카드 인자라고 해도 아무 타입 변수나 사용할 수 있는 건 아니다.
클래스에서 Number를 상속받았기 때문에, Number와 그 subclass의 타입 변수만 사용 가능하다.
- 와일드카드 한정
class TwoD {} class ThreeD extends TwoD {} class FourD extends ThreeD {} class Coords<T extends TwoD> { T[] coords; Coords(T[] o) { coords = o; } } class BoundedWildcard { static void showXY(Coords<?> c) { // 1 System.out.println("X Y Coordinates:"); for(int i=0; i < c.coords.length; i++) System.out.println(c.coords[i].x + " " + c.coords[i].y); } static void showXYZ(Coords<? extends ThreeD> c) { // 2 System.out.println("X Y Z Coordinates:"); for(int i=0; i < c.coords.length; i++) System.out.println(c.coords[i].x + " " + c.coords[i].y + " " + c.coords[i].z); } static void showAll(Coords<? extends FourD> c) { // 3 System.out.println("X Y Z T Coordinates:"); for(int i=0; i < c.coords.length; i++) System.out.println(c.coords[i].x + " " + c.coords[i].y + " " + c.coords[i].z + " " + c.coords[i].t); } }
1번 코드에서 Coords<?>는 Coords클래스에서 TwoD를 상속받았기 때문에 TwoD, ThreeD, FourD 모두 인자로 들어올 수 있다.
2번 코드는 와일드카드 인자를 ThreeD로 한정했기 때문에 ThreeD, FourD만 인자로 들어올 수 있다.
3번 코드는 와일드카드 인자를 FourD로 한정했기 때문에 FourD만 인자로 들어올 수 있다.
static void showAll(Coords<? extends ThreeD super FourD> c) {}
extends로 상한을 설정했다면, super로 하한을 설정할 수 있다.
위의 코드는 와일드카드 인자로 ThreeD ~ FourD만 인자로 한정한다.
'대학 > 객체지향프로그래밍' 카테고리의 다른 글
java lambda expression / 메서드 참조 (0) 2022.12.10 java generic(2) (0) 2022.12.10 java transient / volatile / instanceof / native / assert (0) 2022.12.09 java I/O (0) 2022.12.09 java 열거형 / 박싱 / 어노테이션 (0) 2022.12.09