Skip to content

Latest commit

 

History

History
312 lines (262 loc) · 6.61 KB

220311.md

File metadata and controls

312 lines (262 loc) · 6.61 KB

지네릭스(Generics)

컴파일시 타입을 체크해 주는 기능

ArrayList<Tv> tvList = new ArrayList<Tv>();

tvList.add(new Tv());
tvList.add(new Audio()); // 컴파일 에러. Tv 외에 다른 타입은 저장 불가

객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여줌

ArrayList tvLIst = new ArrayList();
tvList.add(new Tv());
Tv t = (Tv)tvList.get(0);
ArrayList<Tv> tvList = new ArrayList<Tv>();
tvList.add(new Tv());
Tv t = tvList.get(0); // 형변환 불필요

타입체크와 형변환 생략이 가능해져 코드가 간결해진다.

타입 변수

클래스를 작성할 때, Object타입 대신 타입 변수(E)를 선언해서 사용
일반클래스 -> 지네릭클래스

public class ArrayList extends AbstractList {
    private transient Object[] elementData;
    public boolean add(Object o) {}
    public Object get(int index) {}
    ...
}
public class ArrayList<E> extends AbstractList<E> {
    private transient E[] elementData;
    public boolean add(E o) {}
    public E get(int index) {}
    ...
}

타입 변수에 대입하기

객체를 생성시, 타입 변수(E) 대신 실제 타입을 지정(대입)

ArryList<Tv> tvList = new ArrayList<Tv>();
// 위의 ArrayList의 E부분에 Tv가 들어간다.

타입 변수 대신 실제 타입이 지정되면, 형변환 생략 가능

지네릭스 용어

지네릭 클래스. 'T의 Box' 또는 'T Box'라고 읽는다.

Box<T> 

타입 변수 또는 타입 매개변수.(T는 타입 문자)

T

원시 타입(raw type)

Box

지네릭 타입과 다형성

참조 변수와 생성자의 대입된 타입은 일치해야 한다.

ArrayList<Tv> list = new ArrayList<Tv>();
ArrayList<Product> list = new ArrayList<Tv>(); // 에러

지네릭 클래스간의 다형성은 성립.(여전히 대입된 타입은 일치해야 함)

List<Tv> list = new ArrayList<Tv>();
List<Tv> list = new LinkedList<Tv>();

매개변수의 다형성도 성립

ArrayList<Product> list = new ArrayList<Product>();
list.add(new Product());
list.add(new Tv());     // Product의 자손
list.add(new Audio());  // Product의 자손

사용 예

package ch02;

import java.util.*;
import static java.util.Collections.*;

class Product {}
class Tv extends Product {}
class Audio extends Product {}

public class EX3 {

	public static void main(String[] args) {
		ArrayList<Product> productList = new ArrayList<Product>();
		ArrayList<Tv> tvList = new ArrayList<Tv>();
		
		productList.add(new Tv());
		productList.add(new Audio());
		
		tvList.add(new Tv());
		tvList.add(new Tv());
		
		printAll(productList);
	}

	public static void printAll(ArrayList<Product> list) {
		for (Product p : list) {
			System.out.println(p);
		}
	}
	
}

Iterator<E>

클래스를 작성할 때, Object 타입 대신 T와 같은 타입 변수를 사용

public interface Iterator {
    boolean hasNext();
    Object next();
    void remove();
}

Iterator it = list.iterator();
while(it.hasNext()) {
    Student s = (Student)it.next();
    ...
}
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

Iterator<Student> it = list.iterator();
while(it.hasNext()) {
    Student s = it.next();
    ...
}

예시

package ch02;

import java.util.*;
import static java.util.Collections.*;

public class EX3 {

	public static void main(String[] args) {
		ArrayList<Student> list = new ArrayList<Student>();
		list.add(new Student("A", 1, 1));
		list.add(new Student("B", 1, 2));
		list.add(new Student("C", 2, 1));
		
		Iterator<Student> it = list.iterator();
		while(it.hasNext()) {
			Student s = it.next();
			System.out.println(s.name);
		}
	}
	
}

class Student {
	String name = "";
	int ban;
	int no;
	
	Student(String name, int ban, int no){
		this.name = name;
		this.ban = ban;
		this.no = no;
	}
}

A
B
C

HashMap<K, V>

여러 개의 타입 변수가 필요한 경우, 콤마를 구분자로 선언

package ch02;

import java.util.*;
import static java.util.Collections.*;

public class EX3 {

	public static void main(String[] args) {
		HashMap<String, Student> map = new HashMap<>();
		map.put("A", new Student("a", 1, 1, 100, 100, 100));
		
		Student s = map.get("A");
		
		System.out.println(s.name);
	}
	
}

class Student {
	String name = "";
	int ban;
	int no;
	int kor;
	int eng;
	int math;
	
	Student(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;
		
	}
}

a

제한된 지네릭 클래스

extends로 대입할 수 있는 타입을 제한

class FruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정가능
    ArrayList<T> list = new ArrayList<T>();
    ...
}

FruitBox<Apple> appleBox = new FruitBox<Apple>();

인터페이스인 경우에도 extends를 사용

interface Eatable {}
class FruitBox<T extends Eatable> {...}

예시

package ch02;

import java.util.*;
import static java.util.Collections.*;

public class EX3 {

	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.add(new Fruit());
		fruitBox.add(new Apple());
		fruitBox.add(new Grape());
		appleBox.add(new Apple());
		
		System.out.println(fruitBox);
		System.out.println(appleBox);
	}
	
}

interface Eatable {}

class Fruit implements Eatable {
	public String toString() {return "Fruit";}
}

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"; }}

class FruitBox<T extends Fruit & Eatable> extends Box<T> {}

class Box<T> {
	ArrayList<T> list = new ArrayList<T>();
	void add(T item) { list.add(item); }
	T get(int i) { return list.get(i); }
	int size() { return list.size(); }
	public String toString() { return list.toString(); }
}

지네릭스의 제약

타입 변수에 대입은 인스턴스 별로 다르게 가능

Box<Apple> appleBox = new Box<Apple>();
Box<Grape> grapeBox = new Box<Grape>();

static멤버에 타입 변수 사용 불가
모든 인스턴스가 공통으로 사용하기 때문

class Box<T> {
    static T item; // 에러
}

배열 생성할 때 타입 변수 사용불가. 타입 변수로 배열 선언은 가능

class Box<T> {
    T[] itemArr;
    ...
    T[] toArray() {
        T[] tmpArr = new T[itemArr.length]; // 에러
    }
}