bdfgdfg
제네릭스 & 어노테이션 본문
제네릭스
제네릭스란 다양한 타입의 객체들을 다루는 메소드나 컬렉션 클래스에 컴파일 시의 타입체크를 해주는 기능이다.
-> 객체의 타입을 컴파일시에 체크하기에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다.
class Box<T>
{
T item;
void setItem(T item ) { this.item = item;}
T getItem() {return item;}
}
제네릭의 사용방법은 위와 같다.
T에 해당하는 문자는 다른 문자가 와도 상관없다. 기존에는 다양한 종류의 타입을 다룰 때 Object타입을 사용했고, 그로인해 형변환이 불가피 했지만 이제는 제네릭을 이용해 원하는 타입을 지정하여 사용만 하면 된다.
import java.io.FileInputStream;
import java.util.*;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
class Box<T>
{
T item;
void setItem(T item ) { this.item = item;}
T getItem() {return item;}
}
public class Hello {
public static void main(String[] args)
{
Box<String> b= new Box<String>();
b.setItem( "Item");
b.setItem(1); // 에러!
b.setItem(new Object()); // 에러! String타입 이외에 타입은 불가능
String item = b.getItem(); // 형변환 불필요
}
}
다만 내부에서 제네릭 배열의 선언은 가능하지만 생성은 불가능하다.
그 이유는 new연산자는 컴파일 시점에 해당 T가 어떤 타입이 될지 모르기 때문. 마찬가지로 instanceof연산자도 T를 피연산자로 사용할 수 없다.
만약 제네릭 배열을 생성해야할 경우 new연산자 대신 Reflection API의 newInstance와 같이 동적으로 객체를 생성하는 메소드로 배열을 생성하거나, Object배열을 생성해서 복사한다음에 T[]로 형변환하는 방법을 사용한다.
import java.io.FileInputStream;
import java.util.*;
import java.time.*;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
class Animal
{
}
class Cat extends Animal
{
}
class Box<T>
{
private ArrayList<T> arrayList = new ArrayList(); // 생성시 <T>생략가능
public void addItem(T item) throws Exception
{
if(arrayList == null)
throw new Exception();
arrayList.add(item);
}
}
public class Hello {
public static void main(String[] args)
{
// 제네릭 객체 생성 시 넘겨주는 타입이 자손관계더라도 타입지정이 불가능
//Box<Animal> s = new Box<Cat>(); // 에러!
Box<Animal> s2 = new Box<Animal>();
try
{
// 단 자손관계에 있는 객체 삽입은 가능.
s2.addItem(new Animal());
s2.addItem(new Cat());
}
catch( Exception e )
{
e.printStackTrace();
}
}
}
제한된 제네릭 클래스
타입 매개변수 T에 지정할 수 있는 타입의 종류를 제한할 수 있다.
class Box<T extends Animal>
와일드 카드
메소드에서 제네릭 타입을지정해서 매개변수로 선언을 할 수 있다.
단 제네릭 타입은 컴파일러가 컴파일할 때만 사용하고 제거해버리기에 밑의 코드를 컴파일 하면 컴파일 에러가 발생한다.
static Juice makeJuice(FruitBox<Fruit> box)
{
}
static Juice makeJuice(FruitBox<Apple> box)
{
}
이럴 때 사용하기 위해 고안된 것이 와일드 카드.
와일드 카드는 기호 ?로 표현하며 어떠한 타입도 될 수 있다.
?만으로는 Object타입과 다를게 없으므로 다음과 같이 extends와 super로 상한과 하한을 제한할 수 있다.
<? extends T> 와일드 카드의 상한 제한. T와 그 자식들만 가능
<? super T> 와일드카드의 하한 제한. T와 그 부모들만 가능
<?> 제한없음. 모든 타입이 가능하며 <? extends Object>와 동일하다
static Juice makeJuice(FruitBox<? extends Fruit> box)
{
}
그렇기에 위와 같이 정의하여 메소드 오버로딩할 필요없이 작성이 가능하다.
어노테이션(annotation)
어노테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서, 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것.
표준 어노테이션
여기서 매우 자주 쓰일 어노테이션은 오버라이딩과 관련된 @Override 어노테이션이다.
자바 언어는 특징상 오버라이딩에 특별한 문법이 존재하지않는다.(virtual,override)
그렇기에 사용자의 실수로 오버라이딩을 의도하여 메소드를 작성했지만 실수로 해당 메소드가 오버라이딩 되지 않을경우 컴파일러는 알아차리지 못하기에 문제를 뒤늦게 발견할 수 있다.
그러나 @Override 어노테이션을 사용하면 컴파일러가 같은 이름의 메소드가 부모에 있는지 확인하고 없다면 에러메시지를 출력한다.
@Override를 붙이는건 강제되는건 아니지만 하나의 문법처럼 여기고 꼭 해주는게 좋다.
또 하나 알아볼 건
@SuppressWarnings 어노테이션.
이것은 컴파일러가 보여주는 경고메시지가 나타나지 않게 억제해준다.
@SuppressWarnings로 억제할 수 있는 경고메시지의 종류는 여러가지가 있지만 주로 쓰이는건 deprecation,unchecked,rawtypes,varargs정도.
deprecation : @Deprecated가 붙은 대상을 사용해서 발생하는 경고를 억제
unchecked : 제네릭스로 타입을 지정하지 않을 때 발생하는 경고를 억제
rawtypes : 제네릭스를 사용하지 않아서 발생하는 경고를 억제
varargs : 가변인자 타입이 제네릭 타입일 떄 발생하는 경고를 억제.
위 경고를 SuppressWarings 어노테이션을 이용해 없애보자.
이제는 경고가 발생하지 않는다.
참고로 여러 종류를 지정하고 싶다면 @SuppressWarnings({"rawtypes","unchecked"})와 같이 쓰면 된다.
다만 저렇게 ArrayList위에만 작성하면 범위가 좁으므로 메소드 위에다가 쓰면 메소드안의 경고를 지정한 억제경고들을 모두 무시할 수 있다.
'웹프로그래밍 > Java' 카테고리의 다른 글
람다 (0) | 2023.07.30 |
---|---|
쓰레드 (0) | 2023.07.29 |
자바의 컬렉션(Collection) (0) | 2023.07.27 |
Calendar와 Date 그리고 java.time (0) | 2023.07.27 |
자바 java.lang패키지 및 오토박싱&언박싱 (0) | 2023.07.26 |