bdfgdfg

제네릭스 & 어노테이션 본문

웹프로그래밍/Java

제네릭스 & 어노테이션

marmelo12 2023. 7. 29. 13:58
반응형

제네릭스

제네릭스란 다양한 타입의 객체들을 다루는 메소드나 컬렉션 클래스에 컴파일 시의 타입체크를 해주는 기능이다.

 -> 객체의 타입을 컴파일시에 체크하기에 객체의 타입 안정성을 높이고 형변환의 번거로움이 줄어든다.

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)

어노테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서, 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것.

 

표준 어노테이션

https://velog.io/@gkskaks1004/%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98

여기서 매우 자주 쓰일 어노테이션은 오버라이딩과 관련된 @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
Comments