객체 생성시점, 구현클래스 구현시점, 메소드 실행시점에 별칭을 대신할 데이터 타입을 외부에서 지정하는 것이다.
클래스나 인터페이스 옆에 header처럼 적어 놓았다.
제네릭은 <>다이아몬드표기법으로 타입파라미터(별칭)을 지정한다.
객체생성시점, 구현클래스구현시점, 메소드실행시점에 데이터타입을 지정한다.
데이터타입은 클래스 혹은 인터페이스타입만 가능하다.
기본자료형은 제네릭의 데이터타입으로 지정할 수 없다.
제네릭이 나오게 된 이유
Box라는 class를 생성한 후 Box안에 생선도 넣고, 고구마도 넣고, 기계물품도 넣고, 다양한 물건을 넣고 꺼내고 싶었다. 그러나 데이터 타입을 1가지만 정하면 그것밖에 꺼낼 수 없고 , 범용적으로 사용되고 있는 상속되고 있는 타입을 정해서 입력을 하더라도 Phone과 Apple이 함께 출력될 수 없는다.
그럼 저장소의 타입을 모든 객체의 부모인 데이터타입으로 설정하면 어떨까?? 일단 결과는 반은 성공적이었다. 모든 객체는 모든지 클래스 형변환으로 담을 수 있고, 출력도 가능했기 때문이다. 그러나 문제점이 발생했다.
저장되는 객체의 종류를 지정할 수 없다. (타입의 안정성이 확보되지 않는다.)
저장된 객체를 출력할 때마다 형변환이 필요하다.(강제형변환이 필요하다.)
장점?? Box의 객체는 모든 종류의 객체를 저장할 수 있다.
Food객체를 여러개 저장하는 NoneGenericBox의 객체를 생성하였다.
생성된 NoneGenericBox 객체에 food객체를 저장한다.
void addItem(Object item) {...}이 실행된다.
그리고 저장될 때 Object 타입으로 클래스 형변환이 된다.
NoneGenericBox 에 다른타입을 입력해도 에러가 나지 않는다 .
생성된 Box객체에 Food객체와 다른 객체를 저장해도 오류가 발생하지 않는다.(타입의 안정성이 보장되지 않는다.)
NoneGenericBox 객체에 저장된 Food객체를 가져온다.
그럼 Object의 객체의 주소값이 반환된다.
Object getItem(int index){...}가 실행된다.
Food타입으로 강제 형변환한다. 형변환 연산자를 꼭 사용해야한다.
강제형변환을 꼭 사용해야한다.
이 부분을 극복하기 위해서 제네릭을 사용하기로 하였다.
타입 결정에 제한적이다.
안정성이 확보되지 않았다.
형변환 사용을 해야한다.
위의 이유때문에 문제가 있다면..? 그럼 처음부터 타입을 정하지 말자.
제네릭
위에서 Object를 쓴 부분을 <T>로 바꾸고 Object의 부분을 T로 지정해 놓으면 된다.
T = 타입파라미터
그리고 GenericBox는 main메소드가 있는 App객체에 생성을 할 때 타입파라미터를 사용하기 바로 직전에 작성을 해주면 된다. 그렇게 되면
이렇게 T로 설정한 곳이 사용할 객체를 넣자마자 바로 Food, Contact사용할 객체의 데이터 타입으로 변한것을 확인할 수 있다.
위의 T의 자리에 실제로 생성된 객체 데이터 타입이 들어가 있다.
<T>자리에 들어가는 것만 쓰기전에 정해주면 되는 class가 제네릭 class이다.
Object 처럼 고정이아니라 사용 바로 직전에 그 타입으로 정해지는 것이다.
언제 사용될까? 객체를 저장하는 collection클래스에 거의 사용된다.
타입파라미터란 - 필드의 타입, 매개변수의 타입, 리턴타입이다.
Box<int > numbers = new Box<int>();이것은 사용이 불가하다.
int를 담고 싶다면, Box<Integer> numbers = new Box<>();이렇게 사용이 가능하다.
제네릭 클래스
타입파라미터를 하나이상 가지고 있는 클래스이다.
여러 종류의 객체를 다루는 클래스를 제네릭 클래스로 정의하면 타입의 안전성을 보장받고, 형변환코드를 제거할 수 있다.
제네릭 클래스는
자료구조(Set, List, Map<K, V>)
데이터를 반복처리하는 것 에서 사용한다.
제네릭 클래스는 <T>를 지정하지 않으면, 생략한다면 Object의 타입으로 변하게 된다.
타입파라미터로 설정한 부분이 전부 Object타입으로 변하게 된다.
타입의 안정성이 보장되지 않는다.
저장된 객체를 사용할 때에는 형변환 연산자를 사용해야한다.
제네릭을 사용하면 이렇게 쉽게 코드를 작성할 수 있다.
이제 타입에 따른 객체를 생성할 수 있다.
타입지정이라는 것은 없다. Generic<T> 타입파라미터에 내가 실제로 저장하고 싶은 타입을 저장해준다.
타입안정성으로 오류가 나지 않는다.
값을 가져올때 형변환이 필요없다.
컴파일이 되는 시점을 알 수 있다. (error의 발생, 빨간줄이 가는 것을 보고 확인할 수 있다. )
자바의 변화
1.4 -----> 1.5( 자바 5 버젼 )
오토박싱/ 언박싱
향상된 for문
제네릭
1.7 -----> 1.8(자바 8버젼)
람다표현식 ->
Strim도입
인터페이스 변화(추상메소드가 아닌걸 추구할 수 있게됨)
1.8 -----> 1.9(자바 9버젼)
모듈의 도입, 라이브러리를 도입했다.
제네릭의 extends
전체 객체만 아닌 Phone타입만 담고싶을 때 사용하는
타입파라미터에 지정하는 타입을 제한하는 방법이다.
T의 자리에 Phone에게 extends하고 있는 객체를 가져올 수 있다. 라는 뜻이다.
그렇기 때문에 PhoneGenericBoxApp에 나온 Phone ,SmartPhone, FeaturePhone의 객체를 호출해도 error가 나지 않고, Food객체나 구현, 상속객체가 아니면 error가 난다.
한정시키고 싶을 때 사용한다.
<T extends Phone> : 부모를 포함한 하위객체라는 뜻이다.
제네릭의 와일드 카드
물음표가 들어가는 방법이다.
작성방법
<? extends T>
<? super T>
<?>
제네릭의 다형성을 적용할 때 사용한다.
'?','extends','super'를 사용해서 다룰 객체의 상한과 하한을 지정할 수 있다.