Back-End/Java

자바의 정석 12장: 지넀늭슀, 엎거형, 애너테읎션

챔🐻 2024. 1. 24. 15:38
2⃣

[ 출처 ]
자바의 정석:
https://product.kyobobook.co.kr/detail/S000001550352
자바의 정석 유튜람:
https://www.youtube.com/playlist?list=PLW2UjW795-f6xWA2_MUhEVgPauhGl3xIp

12-1 지넀늭슀(Generics)란?

  • 컎파음 시 타입을 첎크핎 죌는 Ʞ능(compile-time type check) - JDK1.5
// Tv객첎만 저장할 수 있는 ArrayList륌 생성
ArrayList<Tv> tvList = new ArrayList<Tv>();

tvList.add(new Tv());    // OK
tvList.add(new Audio()); // 컎파음 에러. Tv 왞에 닀륞 타입은 저장 불가 

//Tv t = (Tv)tvList.get(0);
Tv t = tvList.get(0); // 형변환 불필요

⭐
지넀늭슀의 장점
1. 객첎의 타입 안정성을 제공한닀.
2. 타입첎크와 형변환을 생략할 수 있윌므로 윔드가 간결핎진닀.

<ì°žê³ >

  1. 객첎의 타입 안정성을 높읞닀는 것은 의도하지 않은 타입의 객첎륌 저장하는 것을 막고, 저장된 객첎륌 꺌낎올 때 원래의 타입곌 닀륞 타입윌로 형변환되얎 발생할 수 있는 였류륌 쀄여쀀닀는 것읎닀.

    → 런타임에 발생할 수 있는 에러륌 컎파음 타임에 잡을 수 있닀. (ClassCastException 핎결)

  1. JDK1.5 읎후 지넀늭슀가 도입된 읎후에는 지넀늭슀륌 ꌭ 썚알 한닀.

    → 에러가 나는 것은 아니지만 안 좋은 윔드닀.

12-2 타입 변수

  • 큎래슀륌 작성할 때, Object타입 대신 타입 변수(E)륌 선얞핎서 사용.

    JDK1.5부터 음반큎래슀(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) { /* 낎용생략 */ }
    		...
    }

12-3 타입 변수에 대입하Ʞ

  • 객첎륌 생성 시, 타입 변수(E) 대신 싀제 타입(Tv)을 지정(대입)
    // 타입 변수 E 대신에 싀제 타입 Tv륌 대입
    //       찞조변수                     생성자
    ArrayList<Tv> tvList = new ArrayList<Tv>();
  • 타입읎 대입되고 나멎, ArrayList의 선얞에 포핚된 타입 변수 E가 아래와 같읎 지정된 타입윌로 바뀐닀고 생각하멎 된닀.
    public class ArrayList<E> extends AbstractList<E> {
    	private transient E[] elementData;
    	public boolean add(E o) { /* 낎용생략 */ }
    	public E get(int index) { /* 낎용생략 */ }
    		...
    }

    →

    public class ArrayList extends AbstractList {
    	private transient Tv[] elementData;
    	public boolean add(Tv o) { /* 낎용생략 */ }
    	public Tv get(int index) { /* 낎용생략 */ } // Object가 아닌 Tv륌 반환
    		...
    }
  • 타입 변수 대신 싀제 타입읎 지정되멎, 형변환 생략 가능

    → 형변환만 없얎젞도 윔드륌 훚씬 간결하게 할 수 있닀.

    ArrayList tvList = new ArrayList();
    tvList.add(new Tv());
    Tv t = (Tv) tvList.get(0); // get 반한 type == Object

    →

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

12-4 지넀늭슀 용얎

💡
Box<T> 지넀늭 큎래슀, ‘T의 Box’또는 ‘T Box’띌고 읜는닀.

T 타입 변수 또는 타입 맀개변수. (T는 타입 묞자)

Box 원시 타입(raw type)

12-5 지넀늭 타입곌 닀형성

  • ì°žì¡° 변수와 생성자의 대입된 타입은 음치핎알 한닀.
    ArrayList<Tv> list = new ArrayList<Tv>();      // Ok. 음치
    ArrayList<Product> list = new ArrayList<Tv>(); // 에러. 불음치 / 닀형성처럌 Product와 Tv가 조상 자손 ꎀ계여도 안 된닀.
  • 지넀늭 큎래슀간의 닀형성은 성늜. (여전히 대입된 타입은 음치핎알)
    List<Tv> list = new ArrayList<Tv>();  // Ok. 닀형성. ArrayList가 List륌 구현
    List<Tv> list = new LinkedList<Tv>(); // Ok. 닀형성. ArrayList가 List륌 구현
  • 맀개변수의 닀형성도 성늜.
    ArrayLisst<Product> list = new ArrayList<Product>();
    list.add(new Product());
    list.add(new Tv());      // Product의 자손도 OK.
    list.add(new Audio());   // Product의 자손도 OK.

    상속 계잵도 : Project ← (Tv), (Audio)

    boolean add(E e) { ... }

    →

    // Producet와 ê·ž 자손 객첎 가능(닀형성)
    // Product e = new Tv();
    boolean add(Product e) { ... }
  • 예제 12-1
    package Test;
    
    import java.util.ArrayList;
    import java.util.List;
    
    class Product {}
    class Tv extends Product {}
    class Audio extends Product {}
    
    class Ex12_1 {
        public static void main(String[] args) {
            ArrayList<Product>    productList = new ArrayList<Product>();
            ArrayList<Tv>         tvList = new ArrayList<Tv>();
            // ArrayList<Product> tvList2 = new ArrayList<Tv>(); // 에러
            // List<Tv>              tvList3 = new ArrayList<Tv>(); // OK. 닀형성
    
            productList.add(new Tv());    // public boolean add(Product e) / Product와 ê·ž 자손 OK
            productList.add(new Audio()); // 
    
            tvList.add(new Tv()); // public boolean add(Tv e) / Tv ê·ž 자손 OK
            tvList.add(new Tv());
            // tvList.add(new Audio()); // Tv와 Audio는 얎떠한 ꎀ계도 없Ʞ 때묞에 넣을 수 없음
            
            printAll(productList);
            // printAll(tvList);  // 컎파음 에러가 발생한닀. / ArrayList<Product> P = new ArrayList<Tv>(); 찞조변수의 타입곌 생성자의 타입 불음치
        }
    
        public static void printAll(ArrayList<Product> list) {
            for (Product p : list) {
                System.out.println(p);
            }
        }
    }

12-7 Iterator<E>

  • 큎래슀륌 작성할 때, Object타입 대신 T와 같은 타입 변수륌 사용
    public interface Iterator {
    	boolean hasNext();
    	Object next();
    	void remove();
    }

    →

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

    →

    Iterator<Student> it = list.iterator();
    while (it.hasNext()) {
    	Student s = it.next(); // 형변환 불필요
    }

  • 예제 12-2
    package Test;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    
    class Main {
        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("ë‹€324334", 2, 1));
    
            Iterator<Student> it = list.iterator();
            // Iterator it = list.iterator();
            while (it.hasNext()) {
                // Student s = it.next();
                // Student s = (Student) it.next(); // 지넀늭슀륌 사용하지 않윌멎 형변환 필요.
                System.out.println(it.next().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;
        }
    }

12-8 HashMap<K,V>

  • 여러 개의 타입 변수가 필요한 겜우, 윀마(,)륌 구분자로 ì„ ì–ž
    HashMap<String, Student> map = new HashMap<String, Student>(); // 생성
    map.put("자바", new Student("자바", 1, 1, 100, 100, 100)); // 데읎터 저장

    public class HashMap<K,V> extends AbstractMap<K,V> { // 음부 생략
    			...
    	public V get(Object key) { /* 낎용 생략 */ }
    	public V put(K key, V value) { /* 낎용 생략 */ }
    	public V remove(Object key) { /* 낎용 생략 */ }
    			...
    }

    →

    public class HashMap<K,V> extends AbstractMap<K,V> { // 음부 생략
    			...
    	public Student get(Object key) { /* 낎용 생략 */ }
    	public Student put(String key, Student value) { /* 낎용 생략 */ }
    	public Student remove(Object key) { /* 낎용 생략 */ }
    			...
    }
    Student s1 = (Student) map.get("1-1");

    ↔

    Student s1 = map.get("1-1"); // 형변환 불필요

<추가>

  • JDK1.7부터 생성자에 타입지정 생략 가능
    HashMap<String, Student> map = new HashMap<String, Student>();
    HashMap<String, Student> map = new HashMap<>(); // OK. 

  • 예제 12-2
    package chapter12;
    
    import java.util.*;
    
    class Ex12_2 {
    	public static void main(String[] args) {
            HashMap<String, Student> map = new HashMap<>(); // jdk1.7부터 생성자의 타입지정 생략가능
            map.put("자바왕", new Student("자바왕", 1, 1, 100, 100, 100));
    
            // public Student get(Object key) {
            Student s = map.get("자바왕");
    
            System.out.println(map.get("자바왕").name);
    	} // main
    }
    
    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;
    	}
    }

12-9 제한된 지넀늭 큎래슀

타입 묞자로 사용할 타입을 명시하멎 한 종류의 타입만 저장할 수 있도록 제한할 수 있지만, 귞래도 여전히 몚든 종류의 타입을 지정할 수 있닀는 것에는 변핚읎 없닀. 귞렇닀멎, 타입 맀개변수 T에 지정할 수 있는 타입의 종류륌 제한할 수 있는 방법은 없을까?

  • extends로 대입할 수 있는 타입을 제한
    class FruitBox<T extends Fruit> { // Fruit곌 ê·ž 자손만 타입윌로 지정가능, Integer String 읎런 ê±° 안됚
    // class FruitBox<T> {            // 몚든 타입 가능
    	ArrayList<T> list = new ArrayList<T>();
    	...
    }
    
    FruitBox<Apple> appleBox = new FruitBox<Apple>(); // OK
    FruitBox<Toy>   toyBox = new FruitBox<Toy>();     // 에러. Toy는 Fruit의 자손읎 아님

    여전히 한 종류의 타입만 닎을 수 있지만, Fruit 큎래슀의 자손듀만 닎을 수 있닀는 제한읎 더 추가되었닀.

  • 읞터페읎슀읞 겜우에도 extends륌 사용
    interface Eatable {}
    class FruitBox<T extends Eatable> { ... } // implements 안 씀

  • 예제 12-3

    상속계잵도

    package Test;
    
    import java.util.ArrayList;
    
    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"; }}
    
    interface Eatable {}
    
    public class Ex12_3 {
        public static void main(String[] args) {
            FruitBox<Fruit> fruitBox = new FruitBox();
            FruitBox<Apple> appleBox = new FruitBox();
            FruitBox<Grape> grapeBox = new FruitBox();
            // FruitBox<Grape> grapeBox = new FruitBox<Apple>(); // 에러. 타입 불음치
            // FruitBox<Toy> toyBox = new FruitBox(); // 에러. 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
    }
    
    // Fruit의 자손읎고 Eatable 읞터페읎슀륌 구현한 큎래슀만 가능 / 읞터페읎슀 같읎쓞 땐 '&'
    class FruitBox<T extends Fruit & Eatable> extends Box<T> {}
    
    // Box는 낎부적윌로 ArrayList륌 가지고 있음
    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();}
    }

12-11 지넀늭슀의 제앜

  • 타입 변수에 대입은 읞슀턎슀 별로 닀륎게 가능
    Box<Apple> appleBox = new Box<Apple>(); // OK. Apple객첎만 저장가능
    Box<Grape> grapeBox = new Box<Grape>(); // OK. Grape객첎만 저장가능
  • static멀버에 타입 변수 사용 불가
    class Box<T> {
    	static T item; // 에러
    	static int compare(T t1, T t2) { ... }  // 에러
    }
  • ë°°ì—Ž 생성할 때 타입 변수 사용불가. 타입 변수로 ë°°ì—Ž 선얞은 가능
    class Box<T> {
    	T[] itemArr; // OK. T타입의 배엎을 위한 찞조변수
    		...
    	T[] toArray() {
    		T[] tmpArr = new T[itemArr.length]; // 에러. 지넀늭 ë°°ì—Ž 생성불가
    	}
    	
    }

12-12 와음드 칎드<?>

지넀늭 타입에 닀형성을 적용하는 방법

Q. 와음드칎드는 맀개변수에만 사용?

💡
<? extends T> 와음드 칎드의 상한 제한. T와 ê·ž 자손듀만 가능

<? super T> 와음드 칎드의 하한 제한. T와 ê·ž 조상듀만 가능

<?> 제한 없음. 몚든 타입읎 가능. <? extends Object>와 동음
  • 하나의 ì°žì¡° 변수로 대입된 타입읎 닀륞 객첎륌 ì°žì¡° 가능
    ArrayList<? extends Product> list = new ArrayList<Tv>(); // OK
    ArrayList<? extends Product> list = new ArrayList<Audio>(); // OK
    ArrayList<Product> list = new ArrayList<Tv>(); // 에러. 대입된 타입 불음치
  • 메서드의 맀개변수에 와음드 칎드륌 사용
    static Juice makeJuice(FruitBox<? extends Fruit> box) { // Fruit곌 ê·ž 자손 가능
    	String tmp = "";
    	for (Fruit f : box.getList()) tmp += f + " ";
    	return new Juice(tmp);
    }
    
    // 둘 ë‹€ 가능
    Juicer.makeJuice(new FruitBox<Fruit>());
    Juicer.makeJuice(new FruitBox<Apple>());

  • 예제 12-4
    package Test;
    
    import java.util.ArrayList;
    
    class Fruit { public String toString() { return "Fruit"; } }
    
    class Apple extends Fruit {
        public String toString() {
            return "Apple";
        }
    }
    
    class Grape extends Fruit {
        public String toString() {
            return "Grape";
        }
    }
    
    class Juice {
        String name;
    
        Juice(String name) {
            this.name = name + "Juice";
        }
    
        public String toString() {
            return name;
        }
    }
    
    class Juicer {
        // Fruit곌 ê·ž 자손만 타입변수로 섀정 가능
        static Juice makeJuice(FruitBox<? extends Fruit> box) {
            String tmp = "";
    
            for (Fruit f : box.getList()) {
                tmp += f + " ";
            }
    
            return new Juice(tmp);
        }
    }
    
    class Main {
        public static void main(String[] args) {
            // 저장 타입을 Fruit윌로 제한
            FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
            FruitBox<Apple> appleBox = new FruitBox<Apple>();
    
            fruitBox.add(new Fruit());
            fruitBox.add(new Apple());
            fruitBox.add(new Grape()); // 맀개변수의 닀형성 성늜 자손 Ok.
            appleBox.add(new Apple());
            appleBox.add(new Apple());
    
            System.out.println(Juicer.makeJuice(new FruitBox<Apple>()));
            // static 메서드
            System.out.println(Juicer.makeJuice(fruitBox));
            System.out.println(Juicer.makeJuice(appleBox));
            
            
        }
    }
    
    // 왜 빚간쀄..? 상속을 받윌멎 자식 큎래슀에서 조상의 멀버듀을 쓞 수 있윌니까 지넀늭 타입을 알아알핎서 읞듯?
    
    // Fruit곌 ê·ž 자손만 타입변수로 섀정 가능
    class FruitBox<T extends Fruit> extends Box<T> {}
    
    // 낎부에 ArrayList륌 가지고 있음
    class Box<T> {
        ArrayList<T> list = new ArrayList();
    
        // 반환타입읎 T(제넀늭)
        void add(T item) {
            list.add(item);
        }
    
        T get(int i) {
            return list.get(i);
        }
    
        // ArrayList륌 반환
        ArrayList<T> getList() {
            return list;
        }
    
        int size() {
            return list.size();
        }
    
        @Override
        public String toString() {
            return list.toString();
        }
    }

12-14 지넀늭 메서드

  • 지넀늭 타입읎 선얞된 메서드(타입 변수는 메서드 낎에서만 유횚)
    static <T> void sort(List<T> list, Comparator<? super T> c)
  • 큎래슀의 타입 맀개변수<T>와 메서드의 타입 맀개변수 <T>는 별개
    class FruitBox<T> { // 지넀늭 큎래슀
    		...
    	static <T> void sort(List<T> list, Comparator<? super T> c) { // 지넀늭 메서드
    		...
    	}
    }

    → iv와 lv의 ꎀ계와 같닀고 읎핎하멎 됚 / 메서드 낎에서는 lv가 우선되는 것처럌 지넀늭 메서드 낎에 있는 타입변수가 우선된닀.

  • 메서드륌 혞출할 때마닀 타입을 대입핎알 한닀.(대부분 생략 가능)
    FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
    FruitBox<Apple> appleBox = new FruitBox<Apple>();
    
    System.out.println(Juicer.<Fruit>makeJuice(fruitBox));
    System.out.println(Juicer.<Apple>makeJuice(appleBox));
    static <T extends Fruit> Juice makeJuice(FruitBox<T> box) {
      String tmp = "";
      for (Fruit f : box.getList()) { tmp += f + " "; }
      return new Juice(tmp);
    }

    와음드칎드 = 하나의 찞조변수로 서로 닀륞 타입읎 대입된 여러 지넀늭 객첎륌 닀룚Ʞ 위한 것

    지넀늭 메서드 = 지넀늭 큎래슀처럌 메서드륌 혞출할 때마닀 닀륞 지넀늭 타입을 대입할 수 있게 한 것

    두 개의 용도는 완전히 닀늄 / 와음드칎드가 사용불가능할 때 지넀늭 메서드륌 많읎 쓎닀.

    • ㅇ
  • 메서드륌 혞출할 때 타입을 생략하지 않을 때는 큎래슀 읎늄 생략 불가

    → 생략 안 하는 겜우 거의 없음 신겜 안 썚도 됚

    System.out.println(<Fruit>makeJuice(fruitBox));        // 에러. 큎래슀 읎늄 생략불가
    System.out.println(this.<Fruit>makeJuice(fruitBox));   // OK
    System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); // Ok

12-15 지넀늭 타입의 형변환

  • 지넀늭 타입곌 원시 타입 간의 형변환은 바람직하지 않닀.(겜고 발생)
    Box<Object> objBox = null;  
    Box box = (Box) objBox;     // OK. 지넀늭 타입 -> 원시 타입. 겜고 발생
    objBox = (Box<Object>) box; // OK. 원시타입 -> 지넀늭 타입. 겜고 발생
    
    objBox = (Box<Object>) strBox; // 에러. Box<String> -> Box<Object>
    strBox = (Box<String>) objBox; // 에러. Box<Object> -> Box<String>
    // Box<String> b = new Box<Object>(); // 가 안 되는 맥띜곌 같음
    • // 같은 뜻임
      Box b = new Box<String>();
      b.add(new Integer(100));
      //Box b = (Box) new Box<String>();
      //bStr = (Box<String>) b; // Box -> Box<String> 가능 but 겜고
  • 와음드 칎드가 사용된 지넀늭 타입윌로는 형변환 가능
    // Box<Object> objBox = (Box<Object>) new Box<String>(); // 에러. 형변환 불가능
    Box<? extends Object> wBox = (Box<? extends Object>) new Box<String>(); // OK
    Box<? extends Object> wBox = new Box<String>(); // 위 묞장곌 동음
    // Object와 ê·ž 자손 가능
    // 맀개변수로 FruitBox<Fruit>, FruitBox<Apple>, FruitBox<Grape> 등읎 가능
    static Juice makeJuice(FruitBox<? extends Fruit> box) {
    	FruitBox<? extends Fruit> box = new FruitBox<Fruit>(); // OK (FruitBox<? extends Fruit>) 윌로 형변환읎 가능하닚 뜻읎닀.
    	FruitBox<? extends Fruit> box = new FruitBox<Apple>(); // OK
    }
  • Ex12_3.java
    // FruitBox<Apple> -> FruitBox<? extends Fruit> 가능
    FruitBox<? extends Fruit> abox = new FruitBox<Apple>();
    
    // FruitBox<? extends Fruit> -? FruitBox<Apple> 가능? 가능!!!
    FruitBox<Apple> appleBox = (FruitBox<Apple>) abox; // OK. 겜고발생

12-16 지넀늭 타입의 제거

컎파음러는 지넀늭 타입을 제거하고, 필요한 곳에 형변환을 넣는닀. → 컎파음된 파음(*.class)에는 지넀늭 타입에 대한 정볎가 없닀.

why? 지넀늭읎 도입되Ʞ 읎전(JDK1.5 읎전)의 소슀 윔드와의 혞환성을 유지하Ʞ 위핎서읎닀.(하위혞환성)

  1. 지넀늭 타입의 겜계(bound)륌 제거
    class Box <T extends Fruit>
    	void add(T t) {
    		...
    	} 
    }

    →

    class Box {
    	void add(Fruit t) {
    		...
    	}
    }

    why? JDK1.5 읎전 버전도 묞제없읎 돌아갈 수 있도록 하Ʞ 위핎서(하위혞환성)

    귞래서 지넀늭은 컎파음 타임에만 졎재한닀.

    <T> → Object (Ʞ볞)

    <T extneds Fruit> → Fruit (타입읎 제한된 겜우에는 제한된 타입윌로 )

  1. 지넀늭 타입 제거 후에 타입읎 불음치하멎, 형변환을 추가
    T get(int i) {
    	return list.get(i);
    }

    →

    Fruit get(int i) {
    	return (Fruit) list.get(i); // 반환 타입읎 Object읎Ʞ 때묞에 형변환읎 필요핚
    }

    우늬가 형변환을 생략한닀고 핎서 형변환을 안 핮도 되는 것읎 아니띌 컎파음러가 추가륌 핎쀀닀.

  1. 와음드 칎드가 포핚된 겜우, 적절한 타입윌로 형변환 추가
    static Juice makeJuice(FruitBox<? extends Fruit> box) {
    	String tmp = "" ;
    	
    	for (Fruit f : box.getList()) {
    		tmp += f " ";
    	}
    
    	return new Juice(tmp);
    }

    →

    static Juice makeJuice(FruitBox box) {
    	String tmp = "";
    	Iterator it = box.getList().iterator();
    	
    	while (it.hasNext()) {
    		tmp += (Fruit) it.next() + " "; // 반환 타입읎 Object읎므로 형변환 한닀.
    	}
    
    	return new Juice(tmp);	
    }

ê²°ë¡  ⇒ 컎파음 타입에 지넀늭 타입은 없얎진닀.

12-17 엎거형(enum)

  • ꎀ렚된 상수듀을 같읎 묶얎 놓은 것. Java는 타입에 안전한 엎거형을 제공
    Class Card {
    	static final int CLOVER = 0;
    	static final int HEART = 1;
    	static final int DIAMOND = 2;
    	static final int SPADE = 3;
    	// 칎드 묎늬
    
    	static final int TWO = 0;
    	static final int THREE = 1;
    	static final int FOUR = 2;
    	
    }

    →

    class Card {
        enum Kind { CLOVER, HEART, DIAMOND, SPADE }
        enum Value { TOW, THREE, FOUR }
    
    		final Kind kind;
    		final Value value;
    }

    if (Card.Kind.CLOVER == Card.Value.TWO) // 컎파음 에러. 타입읎 달띌서 비교 불가

    → Java의 엎거형은 값&타입 몚두 첎크

12-18 엎거형(enum)

  • 엎거형을 정의하는 방법
    enum 엎거형읎늄 { 상수명1, 상수명2, ... }
  • 엎거형 타입의 변수륌 선얞하고 사용하는 방법
    enum Direction { EAST, SOUTH, WEST, NORTH }
    
    class Unit {
        int x, y;      // 유닛의 위치
        Direction dir; // 엎거형 읞슀턎슀 변수(iv)륌 ì„ ì–ž
    
        void init() {
            dir = Direction.EAST; // 유닛의 방향을 EAST로 쎈Ʞ화
        }
    }
  • 엎거형 상수의 비교에 ==와 compareTo() 사용 가능
    if (dir == Direction.EAST) { // true
        x++;
    } else if (dir > Direction.WEST) { // 에러 엎거형 상수에 비교연산자 사용 불가
    
    } else if (dir.compareTo(Direction.WEST) > 0)  { // compareTo()는 사용 가능
    
    }

    compareTo() : 왌쪜읎 크멎 양수 / 였륞쪜읎 크멎 음수 / 같윌멎 0

12-19 엎거형의 조상 - java.lang.Enum

  • 몚든 엎거형은 Enum의 자손읎며, 아래의 메서드륌 상속받는닀.
    메서드섀명
    Class<E> getDeclaringClass()엎거형의 Class객첎륌 반환
    String name()엎거형 상수의 읎늄을 묞자엎로 반환
    int ordinal()엎거형 상수가 정의된 순서륌 반환(0부터 시작)
    T valueOf(Class<T> enumType, String name)지정된 엎거형에서 name곌 음치하는 엎거형 상수륌 반환
  • values(), valueOf()는 컎파음러가 자동윌로 추가
    static E[] values()
    static E valueOf(String name)
    
    Direction[] dArr = Direction.values();
    //Direction d = Direction.valueOf("WEST");
    
    for (Direction d : dArr) {
    	System.out.printf("%s=%d\n", d.name(), d.ordinal());
    }

엎거형 상수가 객첎읎Ʞ 때묞에 비교 연산자륌 쓞 수 없고 compareTo()와 equals()륌 쓎닀.

  • 예제 12-5
    package chapter12;
    
    enum Direction { EAST, SOUTH, WEST, NORTH }
    
    class Ex12_5 {
    	public static void main(String[] args) {
    		Direction d1 = Direction.EAST;
    		Direction d2 = Direction.valueOf("WEST");
    		Direction d3 = Enum.valueOf(Direction.class, "EAST");
    
    		System.out.println("d1="+d1);
    		System.out.println("d2="+d2);
    		System.out.println("d3="+d3);
    
    		System.out.println("d1==d2 ? "+ (d1==d2));
    		System.out.println("d1==d3 ? "+ (d1==d3));
    		System.out.println("d1.equals(d3) ? "+ d1.equals(d3)); // 하나하나가 객첎읎Ʞ 때묞에 equals로 비교가 가능하닀.
    //		System.out.println("d2 > d3 ? "+ (d1 > d3)); // 에러
    		System.out.println("d1.compareTo(d3) ? "+ (d1.compareTo(d3)));
    		System.out.println("d1.compareTo(d2) ? "+ (d1.compareTo(d2)));
    
    		switch(d1) {
    			case EAST: // Direction.EAST띌고 쓞 수 없닀.
    				System.out.println("The direction is EAST."); break;
    			case SOUTH:
    				System.out.println("The direction is SOUTH."); break;
    			case WEST:
    				System.out.println("The direction is WEST."); break;
    			case NORTH:
    				System.out.println("The direction is NORTH."); break;
    			default:
    				System.out.println("Invalid direction."); break;
    		}
    
    		Direction[] dArr = Direction.values(); // 엎거형의 몚든 상수륌 배엎로 반환.
    
    		for(Direction d : dArr)  // for(Direction d : Direction.values()) 
    			System.out.printf("%s=%d%n", d.name(), d.ordinal()); // 순서 
    	}
    }

12-20 엎거형에 멀버 추가하Ʞ

  • 불연속적읞 엎거형 상수의 겜우, 원하는 값을 ꎄ혞()안에 적는닀.
    enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10) } // () 생성자 혞출ZZ v
  • ꎄ혞()륌 사용하렀멎, 읞슀턎슀 변수와 생성자륌 새로 추가핎 쀘알 한닀.
    enum Direction {
        EAST(2), SOUTH(3), WEST(-1), NORTH(10); // 끝에 ';'륌 추가핎알 한닀.
    
        private final int value; // 정수륌 저장할 필드(읞슀턎슀 변수)륌 추가
    
        // 생성자륌 추가, private 생략(생성하는 항상 private읎Ʞ 때묞)
        Direction(int value) { this.value = value; }
    
        public int getValue() { return value; }    
    }
  • 엎거형의 생성자는 묵시적윌로 private읎므로, 왞부에서 객첎 생성불가
    Direction d = new Direction(1); // 에러. 엎거형의 생성자는 왞부에서 혞출불가.

  • 예제 12-6
    package chapter12;
    
    enum Direction2 { 
    	EAST(1, ">"), SOUTH(2,"V"), WEST(3, "<"), NORTH(4,"^");
    
    	private static final Direction2[] DIR_ARR = Direction2.values();
    	private final int value;
    	private final String symbol;
    
    	Direction2(int value, String symbol) { // ì ‘ê·Œ 제얎자 private읎 생략됚
    		this.value  = value;
    		this.symbol = symbol;
    	}
    
    	public int getValue()     { return value;  }
    	public String getSymbol() { return symbol; }
    
    	public static Direction2 of(int dir) {
            if (dir < 1 || dir > 4) 
                throw new IllegalArgumentException("Invalid value :" + dir);
    
            return DIR_ARR[dir - 1];
    	}	
    
    	// 방향을 회전시킀는 메서드. num의 값만큌 90도씩 시계방향윌로 회전한닀.
    	public Direction2 rotate(int num) {
    		num = num % 4;
    
    		if(num < 0) num +=4; // num읎 음수음 때는 시계반대 방향윌로 회전 
    
    		return DIR_ARR[(value-1+num) % 4];
    	}
    
    	public String toString() {
    		return name()+getSymbol();
    	}
    } // enum Direction2
    
    
    class Ex12_6 {
    	public static void main(String[] args) {
    		for(Direction2 d : Direction2.values()) 
    			System.out.printf("%s=%d%n", d.name(), d.ordinal()); 
    
    		Direction2 d1 = Direction2.EAST;
    		Direction2 d2 = Direction2.of(2);
    
    		System.out.printf("d1=%s, %d%n", d1.name(), d1.getValue());
    		System.out.printf("d2=%s, %d%n", d2.name(), d2.getValue());
    		System.out.println(Direction2.EAST.rotate(1));
    		System.out.println(Direction2.EAST.rotate(2));
    		System.out.println(Direction2.EAST.rotate(-1));
    		System.out.println(Direction2.EAST.rotate(-2));
    	}
    }

12-23 애너테읎션읎란?

  • 죌석처럌 프로귞래밍 얞얎에 영향을 믞치지 않윌며, 유용한 정볎륌 제공
  • 애너테읎션의 사용예
    // JUnit 닚위 테슀튞 프로귞랚
    @Test // 읎 메서드가 테슀튞 대상임을 테슀튞 프로귞랚에게 알늰닀.
    public void method() {
    	...
    }

12-24 표쀀 애너테읎션

  • Java에서 제공하는 애너테읎션

12-25 @Override

  • 였버띌읎딩을 올바륎게 했는지 컎파음러가 첎크하게 한닀.
  • 였버띌읎딩할 때 메서드 읎늄을 잘못적는 싀수륌 하는 겜우가 ë§Žë‹€.
    class Parent {
    	void parentMethod() { }
    }
    
    class Child extends Parent {
    	vodi parentmethod() { } // 였버띌읎딩하렀 했윌나 싀수로 읎늄을 잘못적음
    }
  • 였버띌읎딩할 때는 메서드 ì„ ì–žë¶€ 앞에 @Override륌 붙읎자.
    class Child extends Parent {
    	void parentMethod() { }
    }

    →

    class Child extends Parent {
    	@Override
    	void parentmethod() { }
    }

12-26 @Deprecated

  • 앞윌로 사용하지 않을 것윌로 권장하는 필드나 메서드에 붙읞닀.
  • @Deprecated의 사용 예, Date큎래슀의 getDate()
    @Deprecated
    public int getDate() {
        return normalize().getDayOfMonth();
    }
  • Deprecated가 붙은 대상읎 사용된 윔드륌 컎파음하멎 나타나는 메시지

    에러 X, 겜고 O

12-27 @FunctionalInterface

  • 핚수형 읞터페읎슀에 붙읎멎, 컎파음러가 올바륎게 작성했는지 첎크

    핚수형 읞터페읎슀에는 하나의 추상메서드만 가젞알 한닀는 제앜읎 있음

    @FunctionalInterface
    public interface Runnable {
    	public abstract void run(); // 추상메서드
    }

12-27 @SuppressWarnings

  • 컎파음러의 겜고메시지가 나타나지 않게 억제한닀.
  • ꎄ혞()안에 억제하고자하는 겜고의 종류륌 묞자엎로 지정
    @SuppressWarnings("unchecked")    // 지넀늭슀와 ꎀ렚된 겜고륌 억제
    ArrayList list = new ArrayList(); // 지넀늭 타입을 지정하지 않았음
    list.add(obj);                    // 여Ʞ서 겜고가 발생

    컎파음러가 알렀죌는 겜고륌 볎고 낎가 확읞했닀는 뜻

  • 둘 읎상의 겜고륌 동시에 억제하렀멎 닀음곌 같읎 한닀.
    @SuppressWarnings ({"deprecation", "unchecked", "varargs", "rawypes"})
  • ‘Xlint’옵션윌로 컎파음하멎, 겜고메시지륌 확읞할 수 있닀.

    ꎄ혞[]안읎 겜고의 종류. 아래의 겜우 rawtypes

    • d

12-29 메타 애너테읎션

→ 애너테읎션을 만듀 때 사용

  • 메타 애너테읎션은 ‘에너테읎션을 위한 애너테읎션’
  • 메타 애너테읎션은 java.lang.annotation 팚킀지에 포핚
애너테읎션섀명
@Target애너테읎션읎 적용가능한 대상을 지정하는데 사용한닀.
@Documented애너테읎션 정볎가 javadoc윌로 작성된 묞서에 포핚되게 한닀.
@Inherited애너테읎션읎 자손 큎래슀에 상속되도록 한닀.
@Retention애너테읎션읎 유지되는 범위륌 지정하는데 사용한닀.
@Repeatable애너테읎션을 반복핎서 적용할 수 있게 한닀.(JDK1.8)

12-30 @Target

  • 애너테읎션을 정의할 때, 적용대상 지정에 사용
    @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface SuppressWarnings {
    		String[] value();
    }
    대상 타입의믞
    ANNOTATION_TYPE애너테읎션
    CONSTRUCTOR생성자
    LOCAL_VARIABLE지역변수
    METHOD메서드
    PACKAGE팚킀지
    PARAMETER맀개변수
    TYPE타입(큎래슀, 읞터페읎슀, enum)
    TYPE_PARAMETER타입 맀개변수(JDK1.8)
    TYPE_USE타입읎 사용되는 몚든 ê³³(JDK1.8)
    import static java.lang.annotation.ElementType.*;
    
    @Target({FIELD, TYPE, TYPE_USE})   // 적용대상읎 FIELD, TYPE, TYPE_USE
    public @interface MyAnnotation { } // MyAnnotation을 정의
    
    @MyAnnotation
    class MyClass {
    
        @MyAnnotation
        int i;        // 적용대상읎 FIELD읞 겜우
    
        @MyAnnotation
        MyClass mc;   // 적용대상읎 TYPE_USE읞 겜우
    }

12-31 @Retention

  • 애너테읎션읎 유지(Retention)되는 Ʞ간을 지정하는데 사용
    유지 정책의믞
    SOURCE소슀 파음에만 졎재, 큎래슀파음에는 졎재하지 않음
    CLASS큎래슀 파음에만 졎재, 싀행 시에 사용불가. Ʞ볞값
    RUNTIME큎래슀 파음에 졎재. 싀행 시에 사용가능

    Ʞ볞값 잘 안 씀. source랑 runtime만 알아두멎 된닀.

  • 컎파음러에 의핎 사용되는 애너테읎션의 유지 정책은 SOURCE임
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Override {}

    ex) @Override - 였버띌읎딩 첎크 / 컎파음러가 첎크하고 끝읎Ʞ 때묞에 싀행 시에 필요 없음ㄎ

  • 싀행 시에 사용 가능한 애너테읎션의 정책은 RUNTIME읎닀.
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface FunctionalInterface {}

12-32 @Documented, @Inherited

→ 많읎 사용 X, 가볍게 알아만 두멎 됚

  • javadoc윌로 작성한 묞서에 포핚시킀렀멎 @Documented륌 붙읞닀.
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface FunctionalInterface {}
  • 애너테읎션을 자손 큎래슀에 상속하고자 할 때, @Inherited륌 붙읞닀.
    @Inherited                    // @SuperAnno가 자손까지 영향 믞치게
    @interface SuperAnno {}
    
    @SuperAnno
    class Parent {
    }
    
    class Child extends Parent {} // Child에 애너테읎션읎 붙은 것윌로 읞식

12-33 @Repeatable

  • 반복핎서 붙음 수 있는 애너테읎션을 정의할 때 사용
    @Repeatable(ToDos.class) // ToDo애너테읎션을 여러 번 반복핎서 쓞 수 있게 한닀.
    @interface ToDo {
    	String value();
    }
  • @Repeatable읎 붙은 애너테읎션은 반복핎서 붙음 수 있닀.
    @ToDo("delete test codes.");
    @ToDo("override inherited methods");
    class MyClass {
    	...
    }
  • @Repeatable읞 @ToDo륌 하나로 묶을 컚테읎너 애너테읎션도 정의핎알 핹
    @interface ToDos { // 여러 개의 ToDo애너테읎션을 닎을 컚테읎너 애너테읎션 ToDos
    	ToDo[] value();  // ToDo애너테읎션 배엎타입의 요소륌 ì„ ì–ž. 읎늄읎 반드시 value읎얎알 핹
    }

12-34 애너테읎션 타입 정의하Ʞ

메서드읞데 왜 싀제 지정할 때는 변수처럌ㄷ씀?

  • 애너테읎션을 직접 만듀얎 쓞 수 있닀.
    @interface 애너테읎션 읎늄 {
    	타입 요소읎늄(); // 애너테읎션의 요소륌 선얞한닀.
    		...
    }
    @interface DateTime {
    	String yymmdd(); // 날짜
    	String hhmmss(); // 시간
    }
  • 애너테읎션의 메서드는 추상 메서드읎며, 애너테읎션을 적용할 때 지정(순서 X)
    @interface TestInfo {
    	int count();
    	String testedBy();
    	String[] testTools();
    	TestType testType();  // enum TestType { FIRST, FINAL }
    	Datetime testDate();  // 자신읎 아닌 닀륞 애너테읎션(@DateTime)을 포핚할 수 있닀.
    }
    @TestInfo( // 사용예
    	count = 3, testedBy = "Kim",
    	testTools = {"JUnit", "AutoTester"},
    	testTYpe = TestType.FIRST,
    	testDate = @DateTime(yymmdd="160101", hhmmss="235959")
    )
    
    public class NewClass { ... }

12-35 애너테읎션 타입 정의하Ʞ

  • 적용 시 값을 지정하지 않윌멎, 사용될 수 있는 Ʞ볞값 지정 가능(null 제왞)
    @interface TestInfo {
    	int count() default 1; // Ʞ볞값을 1로 지정
    }
    
    @TestInfo // @TestInfo(count=1)곌 동음
    public class NewClass { ... }
  • 요소가 하나읎고 읎늄읎 value음 때는 요소의 읎늄 생략간윌
    @interface TestInfo {
    	String value();
    }
    
    @TestInfo("passed")
    class newClass { ... }
    
  • 요소의 타입읎 배엎읞 겜우, ꎄ혞{}륌 사용핎알 한닀.
    @interface TestInfo {
    	String[] testTools();
    }

    @Test(testTools={"JUnit", "AutoTestar"})
    @Test(testTools={"JUnit"})
    @Test(testTools={}) // 값읎 없을 때는 ꎄ혞{}가 반드시 필요

12-36 몚든 애너테읎션의 조상 - java.lang.annotation.Annotation

  • Annotation은 몚든 애너테읎션의 조상읎지만 상속 불가
    @interface TestInfo extends Annotation { // 에러. 허용되지 않는 표현
    	int count();
    	String testedBy();
    		...
    }
  • 사싀 Annotation은 읞터페읎슀읎닀.
    package java.lang.annotation;
    
    public interface Annotation {
    	boolean equals(Object obj);
    	int hashCode();
    	String toString();
    
    	Class<? extedns Annotation> annotationType(); // 애너테읎션의 타입을 반환
    }

몚든 애너테읎션의 조상은 Annotation 읞터페읎슀읎고 위와 같은 추상 메서드듀을 가지고 있닀.

읎 추상메서드는 구현하지 않아도 사용할 수 있닀. 몚든 애너테읎션은 추상메서드듀을 포핚하고 있는 셈읎며 닀륞 요소듀처럌 구현하지 않고도 사용할 수 있닀.

12-37 마컀 애너테읎션 - Marker Annotation

  • 요소가 하나도 정의되지 않은 애너테읎션
    @Target (ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Override {} //  마컀 애너테읎션. 정의된 요소가 하나도 없닀.
    
    @Target (ElementType.METHOD)
    @Retention(RetentionPolicy/SOURCE)
    public @interface Test {} //마컀 애너테읎션. 정의된 요소가 하나도 없닀.
    

    → 대당한 걎 아니고 귞냥 읎런 읎늄을 붙여서 부륞닀는 것만 알멎 된닀.

    @Test // 읎 메서드가 테슀튞 대상임을 테슀튞 프로귞랚에게 알늰닀.
    public void method() {
    				...
    }
    
    @Deprecated
    public int getDate() {
    		return normalize().getDayOfMonth();
    }

    따로 할당할 값은 없지만 졎재하는 것만윌로도 읎 애너테읎션을 사용하는 프로귞랚에게 충분한 정볎임

12-38 애너테읎션 요소의 규칙

  • 애너테읎션의 요소륌 ì„ ì–ží•  때 아래의 규칙을 반드시 지쌜알 한닀.
    • 요소의 타입은 Ʞ볞형, String enum, 애너테읎션, Class(섀계도 객첎)만 허용됚
    • ꎄ혞()안에 맀개변수륌 ì„ ì–ží•  수 없닀.
    • 예왞륌 ì„ ì–ží•  수 없닀.
    • 요소륌 타입 맀개변수로 정의할 수 없닀.
  • 아래의 윔드에서 잘못된 부분은 묎엇읞지 생각핎볎자.
    @interface AnnoTest {
    	int id = 100; // 상수 OK. default 메서드 X
    	String major(int i, int j); // ꎄ혞 안에 맀개변수 ì„ ì–ž 불가능ㄎ
    	String minor() throws Exception; // 예왞 ì„ ì–ž 안됚
    	ArrayList<T> list; // 요소륌 타입 맀개변수로 정의할 수 없닀.
    }

걍 간닚히 볎고 넘얎가Ʞ.. 왞욞필요 전혀 없음

  • 예제 12-8
    package chapter12;
    
    import java.lang.annotation.*;
    import java.util.ArrayList;
    
    @Deprecated
    @SuppressWarnings("1111") // 유횚하지 않은 애너테읎션은 묎시된닀.
    @TestInfo(testedBy="aaa", testDate=@DateTime(yymmdd="160101",hhmmss="235959"))
    class Ex12_8 {
    	public static void main(String args[]) {
            ArrayList<Object> list = new ArrayList<Object>();
            list.add(3);
            
    		// Ex12_8의 Class객첎륌 얻는닀.
    		Class<Ex12_8> cls = Ex12_8.class;
    
    		TestInfo anno = cls.getAnnotation(TestInfo.class);
    		System.out.println("anno.testedBy()="+anno.testedBy());
    		System.out.println("anno.testDate().yymmdd()=" +anno.testDate().yymmdd());
    		System.out.println("anno.testDate().hhmmss()=" +anno.testDate().hhmmss());
    
    		for(String str : anno.testTools())
    			System.out.println("testTools="+str);
    
    		System.out.println();
    
    		// Ex12_8에 적용된 몚든 애너테읎션을 가젞옚닀.
    		Annotation[] annoArr = cls.getAnnotations();
    
    		for(Annotation a : annoArr)
    			System.out.println(a);
    	} // main의 끝
    }
    
    // @InitParam
    @Retention(RetentionPolicy.RUNTIME)  // 싀행 시에 사용가능하도록 지정 
    @interface TestInfo {
    	int       count()	  	default 1;
    	String    testedBy();
    	String[]  testTools() 	default "JUnit";
    	TestType  testType()    default TestType.FIRST;
    	DateTime  testDate();
    }
    
    @Retention(RetentionPolicy.RUNTIME)  // 싀행 시에 사용가능하도록 지정
    @interface DateTime {
    	String yymmdd();
    	String hhmmss();
    }
    
    enum TestType { FIRST, FINAL }

🌞12장 연습묞제


Uploaded by N2T