Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 자바의 정석 기초편 ch4
- 자바의 정석 기초편 ch5
- 타임리프 - 기본기능
- 스프링 mvc2 - 로그인 처리
- @Aspect
- 자바의 정석 기초편 ch14
- 스프링 입문(무료)
- 2024 정보처리기사 시나공 필기
- 스프링 db1 - 스프링과 문제 해결
- 자바의 정석 기초편 ch9
- 자바의 정석 기초편 ch13
- 자바의 정석 기초편 ch6
- 스프링 mvc2 - 타임리프
- jpa 활용2 - api 개발 고급
- 자바의 정석 기초편 ch7
- jpa - 객체지향 쿼리 언어
- 자바의 정석 기초편 ch1
- 게시글 목록 api
- 자바의 정석 기초편 ch8
- 자바의 정석 기초편 ch2
- 자바의 정석 기초편 ch12
- 자바의 정석 기초편 ch11
- 스프링 mvc1 - 스프링 mvc
- 코드로 시작하는 자바 첫걸음
- 2024 정보처리기사 수제비 실기
- 스프링 mvc2 - 검증
- 스프링 mvc1 - 서블릿
- 스프링 고급 - 스프링 aop
- 스프링 db2 - 데이터 접근 기술
- 자바의 정석 기초편 ch3
Archives
- Today
- Total
나구리의 개발공부기록
자바의 정석 기초편 ch12 - 7 ~ 14[제네릭스 적용 예제(Iterator, HashMap), 제한된 제네릭클래스, 제네릭스의 제약, 와일드카드, 제네릭메서드] 본문
유튜브 공부/JAVA의 정석 기초편(유튜브)
자바의 정석 기초편 ch12 - 7 ~ 14[제네릭스 적용 예제(Iterator, HashMap), 제한된 제네릭클래스, 제네릭스의 제약, 와일드카드, 제네릭메서드]
소소한나구리 2023. 12. 13. 15:131) 제네릭스가 적용된 Iterator<E>
- 기존의 Iterator 인터페이스가 정의 되었을 때는 Object클래스가 직접 쓰였지만 이후에는 제네릭이 적용되어 타입변수로 메서드를 정의하고 있음
- Iterator를 사용 시 제네릭스를 적용하면 형변환 없이 지정한 타입의 참조변수에 참조값을 저장할 수 있음
(1) 예제
- ArrayList와 Iterator에 제네릭스를 적용하여 형변환 없이 값을 출력하는 예제
import java.util.*;
class Ex12_2 {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
list.add(new Student("자바왕", 1, 1));
list.add(new Student("자바짱", 1, 2));
list.add(new Student("홍길동", 2, 1));
Iterator<Student> it = list.iterator(); //<Student>제네릭스 사용
while (it.hasNext()) {
// Student s = (Student)it.next(); // 제네릭스를 사용하지 않으면 형변환 필요.
// Student s = it.next();
// System.out.println(s.name);
System.out.println(it.next().name); // 위2줄을 한줄로
}
} // main
}
class Student {
String name = "";
int ban;
int no;
Student(String name, int ban, int no) {
this.name = name;
this.ban = ban;
this.no = no;
}
}
2) 제네릭스가 적용된 HashMap<K, V>
- 여러개의 타입 변수가 필요한 경우 ,(콤마)를 구분자로 선언함
- HashMap의 내부 코드를 보면 타입 변수를 적용했음에도 get과 remove의 매개변수 타입이 계속 Object인 이유는 내부 메서드의 hash(key)가 Object로 되어있기 때문에 불필요한 형변환은 하지 않았기 때문임
(1) 예제
- JDK1.7부터 객체 생성시 생성자에 입력하는 제네릭타입 지정은 생략이 가능함
- HashMap의 타입을 key에는 String, value에는 Student2로 지정한 예제
import java.util.*;
class Ex12_2_1 {
public static void main(String[] args) {
// JDK1.7부터 생성자의 타입지정은 생략 가능
HashMap<String, Student2> map = new HashMap< >();
map.put("자바왕", new Student2("자바왕", 1, 1, 100, 100, 100));
// public Student2 get(Object key) {
// Student2 s = map.get("자바왕"); // map.get에(Student2)형변환 생략 가능
// System.out.println(s.name);
System.out.println(map.get("자바왕").name); //위 두코드를 한번에
} // main
}
class Student2 {
String name = "";
int ban;
int no;
int kor;
int eng;
int math;
Student2(String name, int ban, int no, int kor, int eng, int math) {
this.name = name;
this.ban = ban;
this.no = no;
this.kor = kor;
this.eng = eng;
this.math = math;
}
}
3) 제한된 제네릭 클래스
- 타입변수에 extends 키워드로 대입할 수 있는 타입을 제한할 수 있음
- 지정한 타입과 그 자손 타입만 대입할 수 있도록 범위를 제한할 수 있음
- 제네릭 타입변수에 범위를 지정할 때는 인터페이스인 경우에도 extends를 사용함
- class FruitBox<T extends Fruit & Eatable> 처럼 제네릭 타입변수의 범위를 지정
- FruitBox의 클래스에는 Fruit클래스와 Eatable인터페이스를 구현한 클래스의 자손만 들어올 수 있음
import java.util.ArrayList;
// 최고조상 Fruit클래스, Eatable 인터페이스를 구현
class Fruit implements Eatable {
public String toString() { return "Fruit";}
}
// Fruit을 상속받은 Apple, Grape 클래스, Toy는 아무것도 아님
class Apple extends Fruit { public String toString() { return "Apple";}}
class Grape extends Fruit { public String toString() { return "Grape";}}
class Toy { public String toString() { return "Toy" ;}}
interface Eatable {}
class Ex12_3 {
public static void main(String[] args) {
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
FruitBox<Grape> grapeBox = new FruitBox<Grape>();
// FruitBox<Grape> grapeBox = new FruitBox<Apple>(); // 에러. 타입 불일치
// FruitBox<Toy> toyBox = new FruitBox<Toy>(); // 에러. Toy는 Fruit의 자손이 아님
fruitBox.add(new Fruit());
fruitBox.add(new Apple());
fruitBox.add(new Grape());
appleBox.add(new Apple());
// appleBox.add(new Grape()); // 에러. Grape는 Apple의 자손이 아님
grapeBox.add(new Grape());
System.out.println("fruitBox-"+fruitBox);
System.out.println("appleBox-"+appleBox);
System.out.println("grapeBox-"+grapeBox);
} // main
}
// FruitBox의 클래스에는 Fruit클래스와 Eatable인터페이스를 구현한 클래스의 자손만 들어올 수 있음
class FruitBox<T extends Fruit & Eatable> extends Box<T> {}
class Box<T> {
ArrayList<T> list = new ArrayList<T>(); // item을 저장할 list
void add(T item) { list.add(item); } // 박스에 item을 저장
T get(int i) { return list.get(i); } // 박스에서 item을 꺼냄
int size() { return list.size(); }
public String toString() { return list.toString();}
}
4) 제네릭스의 제약
(1) 타입 변수에는 인스턴스 별로 타입을 다르게 대입할 수 있음
- static멤버는 모든 인스턴스에 공통으로 사용하는 멤버이기 때문에 타입 변수 사용 불가
- 배열을 생성할 때 타입 변수로 생성할 수 없고 참조변수의 선언은 가능함
- new 다음에는 확정된 타입이 와야 하는데 타입변수는 무슨 타입이 올지 미확정이기 때문
class Box<T> {
static T item; // static 변수에 타입변수를 적용하여 에러발생
static int compare(T t1, t t2) {...} // static 메서드의 매개변수에 타입변수를 적용하여 에러발생
T[] itemArr; // OK - 타입변수 배열인 참조변수 선언은 가능함
T[] toArray(){
T[] tmpArr = new T[itemArr.length]; //에러, new연산자의 뒤에 타입변수를 사용할 수 없음
}
}
5) 와일드카드 < ? >
- 하나의 참조변수로 서로 다른 타입이 대입된 여러 제네릭 객체를 다루기 위함(다형성의 효과를 얻을 수 있음)
- 메서드의 매개변수에도 와일드 카드를 사용 가능함
(1) 와일드카드 종류
- <? extends T> : T와 그 자손들만 사용 가능 (와일드 카드의 상한 제한) - 주로 사용
- <? super T> : T와 그 조상들만 사용 가능 (와일드 카드의 하한 제한)
- < ? > : 제한 없음. 모든 타입이 가능 <? extends Object>와 동일
// 와일드 카드 사용으로 제네릭타입이 다르지만 사용 가능함(Product과 그의 자손)
ArrayList<? extends Product> list = new ArrayList<Tv>();
ArrayList<? extends Product> list = new ArrayList<Audio>();
// 메서드의 매개변수에도 와일드 카드 사용 가능함
static Juice makeJuice(FruitBox<? extends Fruit> box) {
...
}
System.out.println(Juice.makeJuice(new FruitBox<Fruit>())); // OK
System.out.println(Juice.makeJuice(new FruitBox<Apple>())); // OK
(2) 예제
import java.util.ArrayList;
class Fruit2 { public String toString() { return "Fruit";}}
class Apple2 extends Fruit2 { public String toString() { return "Apple";}}
class Grape2 extends Fruit2 { public String toString() { return "Grape";}}
class Juice {
String name;
Juice(String name) { this.name = name + "Juice"; }
public String toString() { return name; }
}
class Juicer {
// Fruit2와 그 자손을 사용할 수 있도록 와일드카드 사용
static Juice makeJuice(FruitBox2<? extends Fruit2> box) {
String tmp = "";
// 향상된 for문
for(Fruit2 f : box.getList())
tmp += f + " ";
return new Juice(tmp);
}
}
class Ex12_4 {
public static void main(String[] args) {
FruitBox2<Fruit2> fruitBox = new FruitBox2<Fruit2>();
FruitBox2<Apple2> appleBox = new FruitBox2<Apple2>();
fruitBox.add(new Apple2());
fruitBox.add(new Grape2());
appleBox.add(new Apple2());
appleBox.add(new Apple2());
System.out.println(Juicer.makeJuice(fruitBox));
System.out.println(Juicer.makeJuice(appleBox));
} // main
}
class FruitBox2<T extends Fruit2> extends Box2<T> {}
class Box2<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) { list.add(item); }
T get(int i) { return list.get(i); }
ArrayList<T> getList() { return list; }
int size() { return list.size(); }
public String toString() { return list.toString();}
}
6) 제네릭메서드
- 메서드에 타입 변수가 선언 된 것, 즉 메서드를 호출할 때마다 메서드의 타입을 지정할 수 있음
- 타입 변수는 메서드 내에서만 유효함
- 클래스의 타입 변수<T>와 메서드의 타입 변수<T>는 별개임
class FruitBox <T> { // 제네릭클래스 <T> 타입 선언
...
// 제네릭 메서드에 있는 타입변수 <T> - 제네릭메서드 안에서만 적용되는 타입변수
// 문자가 일치하여도 클래스와 메서드의 <T>는 각각 다른 타입 변수임(별개임)
static <T> void sort(List<T> list, Comparator<? super T> c) {
...
}
}
- 제네릭 메서드를 사용하려면 메서드를 호출할 때마다 타입을 대입해야 함
- 그러나 대부분은 제네릭 메서드의 타입과, 제네릭 메서드를 호출한 참조변수의 타입이 동일하기 때문에 생략이 가능함
// 메서드를 호출할 때마다 타입을 대입
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
...
// 메서드 호출시 대부분 타입 생략 가능
System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); // <Fruit>생략 가능
System.out.println(Juicer.<Apple>makeJuice(appleBox)); // <Apple>생략 가능
// 제네릭 메서드
static<T extends Fruit> Juice makeJuice(FruitBox<T> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + "";
return new Juice(tmp);
}
- 타입 생략이 불가능하여 타입을 작성해야한다면 클래스이름이나 this를 꼭 사용해야함
- 타입 생략이 불가능한 경우가 드물게 발생함
// 메서드 호출 시 타입을 생략하지 않을 때는 클래스 이름 생략 불가
System.out.println(<Fruit>makeJuice(fruitBox)); // 클래스이름 생략 -> 에러
// this.<Fruit> or 클래스이름.<Fruit> 둘중 하나처럼 작성해야 함
System.out.println(this.<Fruit>makeJuice(fruitBox));
System.out.println(Juicer.<Fruit>makeJuice(fruitBox));
(1) 와일드카드와 제네릭메서드의 목적
- 서로의 목적은 다르지만 와일드카드가 쓸 수 없을때 제네릭메서드를 사용하는 경우가 많음
- 제네릭메서드 - 메서드를 호출할 때마다 다른 제네릭 타입을 대입할 수 있게 한 것
- 와일드카드 - 하나의 참조변수로 서로 다른 타입이 대입된 여러 제네릭스 객체를 다루기 위한 것
** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의
'유튜브 공부 > JAVA의 정석 기초편(유튜브)' 카테고리의 다른 글
자바의 정석 기초편 ch12 - 23 ~ 37 [애노테이션, 표준애노테이션, 메타애노테이션, 애노테이션 타입 정의하기, 애노테이션의 요소] (0) | 2023.12.15 |
---|---|
자바의 정석 기초편 ch12 - 15 ~ 22 [제네릭형변환, 열거형, 열거형의 조상, 열거형에 멤버 추가하기] (0) | 2023.12.14 |
자바의 정석 기초편 ch12 - 1 ~ 6[제네릭스란?, 타입변수, 제네릭스 용어, 제네릭 타입과 다형성] (0) | 2023.12.13 |
자바의 정석 기초편 ch11 - 52 ~ 56[Collections클래스, 컬레션클래스 요약] (0) | 2023.12.12 |
자바의 정석 기초편 ch11 - 46 ~ 51[HashMap과 Hashtable] (1) | 2023.12.12 |