이펙티브 자바에서 제네릭
을 다룬 내용을 나름 정리해보고자 한다.
내용은 아마도.. 계속.. 추가될..ㅇ ㅖ ㅈ ㅓ ㅇ ..
List
이렇게만 선언하는 것이 raw 타입
얘는 타입도 선언 안해주는데 왜 씀? 이라고 물으신다면..
-> (제너릭이 등장하기 전의 코드와의) 호환성
때문!
(이 호환성이라는 것에 대한 정확한 예시는 이해하고 추가해야할 거 같다)
-> 클래스 리터럴와 instanceOf 연산자는 로 형태가 필요,,
(로) List => 제네릭 타입과 무관 => 따라서 List<String>이 하위
(Object) List<Object> => 모든 타입 허용 => but 하위는 X
//따라서 로 타입이라면 타입 안전성을 잃게 됨
literal이라는 단어의 의미가 '문자 그대로'라는 뜻인데, 이는 변수 or 상수에 저장되는 값 그 자체
이다. 또한 원시형(Primitive-int, char), 참조형과 같은 자바의 데이터 타입 중 하나이다.
용도는 class 클래스를 표기하기 위해 사용됨!(정확히는 Class 클래스의 객체를 표기하기 위해)
** class 클래스는 java.lang 패키지에, 프로그램의 클래스와 인터페이스를 관리하는데에 사용된다.
원래 인스턴스 형태가 맞는지 여부를 체크해준다 = object instanceOf type
여기서 object가 type or 상속받는 class라면 true를, 아니면 false를 return해준다.
class Product {}
class Snack extends Product{}
public class instanceOf {
public static void main(String[] args) {
Product p = new Product();
Snack sn = new Snack();
System.out.println(sn instanceOf Product) // true
System.out.println(p instanceOf Snack) // false
}
}
타입이 안전하다는 생각이 들 때면 @Surpresswarnings("unchecked")
애노테이션을 통해 ClassCastException(하위 유형이 아닌 유형 참조시 발생)
의 등장을 막아라!
단, 이 애노테이션의 사용 범위를 줄이고(클래스->하나의 메서드) 사용 이유를 주석으로 적어주면 좋다.
//공변 -> 다형성 찾아보다가 알게 된 사실
//조상 타입의 배열에 하위 객체를 담을 수 있다!
public class Product {
int price;
public Product(int price) {
this.price = price;
}
Product() {}
}
public class Icecream extends Product {
Icecream() {super(1000);}
public String toString() {
return "Icecream";
}
}
public class Snack extends Product {
Snack() {super(2000);}
public String toString() {
return "Snack";
}
}
public class Chicken extends Product {
Chicken() {super(15000);}
public String toString() {
return "Chicken";
}
}
class Main {
public static void main(String[] args) {
Product[] products = {new Icecream(), new Chicken(), new Snack()};
}
}
= 컴파일 시 문제가 안되나, 런타임 시 ArrayStoreException(객체 배열에 잘못된 유형을 넣을 때 두둥등장)
을 발생시킨다.
= 실체화 된다 (=런타임시에도 원소의 타입 체크)
//컴파일 시 오류가 발생하는 예시
Object array[] = new String[10];
-> 배열이 공변이라서 가능한 것!
-> String을 Object의 하위 타입으로 인식하여 컴파일 됨
array[0] = 3;
**무조건 컴파일 시 오류를 잡는 것이 제어에 용이하므로
String[] arr = new String[10];
//이런식으로 타입을 특정지어주는 것이 좋음
리스트 -> 제네릭 O -> 공변 X
= 불공변하기 때문에 당연히 위의 예시는 컴파일 자체가 안됨!
= 서로 다른 제네릭 타입 간에는 상하위 관계가 X
= 타입 안전성이 보장됨
그렇담 왜 배열은 공변일까?
= 다형성
을 위해!
(프로그램의 다양한 요소들이 다양한 자료형에 속하는 것이 허용되는 성질)
제네릭 타입을 활용해 형변환 없이도 설계할 수 있도록 한다.
제네릭 클래스
(1) 타입 매개변수 선언
(2) 일반 타입 -> 타입 매개변수로
(3) static 사용 X (static 선언 시 메모리에 저장되는데 아직 타입 구체화가 되지 않았으므로)
public class Stack<E> {
private E[] element;
public Stack {
element = (E[]) new Object[10];
}
}
제네릭 메서드
(1) 컴파일러에게 내가 제네릭 메서드라는 것을 알려야 하며, 리턴 타입 을 정의하기 전에 반드시 타입을 확실히 명시해줘야 함!
(2) 일반 클래스 에서도 제네릭 메서드를 사용할 수 있음
(3) static 사용 O (호출 시 타입을 지정하므로)
public static<T,V> boolean compare(People<T,V> p1, People<T,V> p2) {
boolean nameCompare = p1.getName().equals(p2.getName());
boolean ageCompare = p1.getAge().equals(p2.getAge());
return nameCompare && ageCompare;
}
이제 두개를 main함수에서 사용할 때
public class ExGeneric {
public static void main(String []args){
People<String,Integer> p1 = new People("SK",24);
People<String,Integer> p2 = new People("YW",25);
boolean result = p1.compare(p1,p2);
System.out.println(result);
}
}
이렇게 타입 파라미터를 지정한 뒤 메서드를 호출해준다!
와일드 카드의 형태만 알고 가자!
List<?> // 선언 시 모든 클래스나 인터페이스 삽입 가능
List<? extends 상위타입> //특정 객체의 상위 클래스만
List<? super 하위타임> //특정 객체의 하위 클래스만
https://hwan33.tistory.com/24
https://recordsoflife.tistory.com/695
https://tecoble.techcourse.co.kr/post/2020-10-27-polymorphism/
(다형성과 오버라이딩, 오버로딩 + a 로 설명하셨는데 나도 공부해야쥐)
https://madplay.github.io/post/effectivejava-chapter5-generics
https://qdgbjsdnb.tistory.com/28
https://coding-factory.tistory.com/573
https://93jpark.tistory.com/118