Generics와 Annotation

서현서현·2022년 4월 14일
0

JAVA

목록 보기
14/27
post-thumbnail

Generics

  • 클래스에 사용할 타입을 설계시가 아니라 클래스 사용시 지정하여 사용하는 기술

Generic Class

제너릭 클래스를 만드는 방법

(형식)
 		class 클래스명<제너릭타입글자> {
 			제너릭타입글자 변수명; // 변수선언에 제너릭을 사용할경우
  			...
  
  			제너릭타입글자 메서드명() {
  				...
  			return;
  		}
  		...
  	}

제너릭타입 글자

T => Type
K => Key
V => Value
E => Element(자료구조에 들어가는 것들을 나타낼 때 사용)

GenericClas 예제

class NonGenericClass{
	private Object val;
	public Object getVal() {
		return val;
	}
	public void setVal(Object val) {
		this.val = val;
	}	
}
// 늘 하던대로 평범하게

class MyGeneric<T>{
//class 클래스명 <제너릭타입글자>
	private T val;
// 제너릭타입글자 변수명
	public T getVal() {
//제너릭타입글자 메서드명
		return val;
	}
	public void setVal(T val) {
		this.val = val;
	}
}

public class T01_GenericClassTest {
	public static void main(String[] args) {
//////////////////////////////////
		NonGenericClass ng1 = new NonGenericClass();
		ng1.setVal("가나다라");
		
		NonGenericClass ng2 = new NonGenericClass();
		ng2.setVal(100);
		
		// 가나다라와 100이 Object타입으로 리턴되기 때문에 
		// String으로 변환해야한다
		
		String rtnVal1 = (String)ng1.getVal();
		System.out.println("문자열 반환값 rtnNg1 => "+rtnVal1);
		
		Integer irtnVal2 = (Integer) ng2.getVal();
		System.out.println("정수 반환값 irtnNg2 => "+irtnVal2);
		System.out.println();
		

//////////////////////////////////
		MyGeneric<String> mg1 = new MyGeneric<String>();
		MyGeneric<Integer> mg2 = new MyGeneric<>();
		
		mg1.setVal("우리나라");
		mg2.setVal(500);
		
		rtnVal1 = mg1.getVal();
		irtnVal2 = mg2.getVal();
		
		
		System.out.println("제너릭 문자열 반환값: "+rtnVal1);
		System.out.println("제너릭 정수형 반환값: "+irtnVal2);
		
	}
	
}

장점

  1. 타입 safety : Integer로 타입을 정해 놨으므로 String 넣으면 오류로 인식한다!
  2. getter로 꺼내면 바로 쓸 수 있음 (캐스팅 필요X)

+) 제너릭 인터페이스는 클래스와 비슷하게 작동한다

Generic Method

  • 파라미터 타입과 리턴타입으로 타입글자를 가지는 메서드

선언방법

리턴타입 앞에 <>기호를 추가하고 타입 파라미터를 기술 후 사용함
public <T> T genericMethod(T o) {	// 제네릭 메소드
		...
}
 
[접근 제어자] <제네릭타입> [반환타입] [메소드명]([제네릭타입] [파라미터]) {
	// 텍스트
}

Generic Method 예제

class Util{
	public static <K,V> boolean compare(Pair<K,V> p1, Pair<K,V>p2) {
		boolean keyCompare = p1.getKey().equals(p2.getKey());
		boolean valueCompare = p1.getValue().equals(p2.getValue());
		return keyCompare && valueCompare;
	}
}

// 멀티타입<K,V>를 가지는 제너릭 클래스

class Pair<K,V>{
	private K key;
	private V value;
	
	public Pair(K key, V value) {
		super();
		this.key = key;
		this.value = value;
	}
	public K getKey() {
		return key;
	}
	public void setKey(K key) {
		this.key = key;
	}
	public V getValue() {
		return value;
	}
	
	// void타입은 제너릭이나 제너릭메소드 아니고 일반메소드. 
	// 타입이 필요해서 잠깐 빌려쓴거
	public void setValue(V value) {
		this.value = value;
	}
	
	// 키와 값을 출력하는 메소드! **지역변수의 KV 타입을 받는애임**
	public <K,V> void displayAll(K key, V val) {
		System.out.println(key+" : "+val);
	}
	
	
}

public class T02_GenericMethodTest {
	public static void main(String[] args) {
		Pair<Integer, String>p1 = new Pair<Integer,String>(1,"홍길동");
		Pair<Integer, String>p2 = new Pair<Integer,String>(1,"홍길동");
		
		boolean result1 = Util.<Integer,String>compare(p1,p2);
		
		if(result1) {
			System.out.println("논리(의미)적으로 동일한 객체임");
		} else {
			System.out.println("논리(의미)적으로 동일한 객체아님");
		}
		
///////////////////////////
		Pair<String,String> p3 = new Pair<String,String>("001","홍길동");
		Pair<String,String> p4 = new Pair<String,String>("002","홍길동");
		boolean result2 = Util.compare(p3, p4);
		
		if(result2) {
			System.out.println("논리(의미)적으로 동일한 객체임");
		} else {
			System.out.println("논리(의미)적으로 동일한 객체아님");
		}
		
		// 제네릭 메서드 테스트
		 p1.displayAll("키",180); //인티저, 스트링이여야 해서 오류 
		// => 제네릭 이용해 해결이 가능하다. displayAll에 제네릭 넣기!
	}

}

제한된 타입 파라미터 (Bounded Parameter) 예제

제너릭에서 오브젝트는 타입임! 평소에 오브젝트는 최상위타입이므로 어떤타입이든 다 들어 갈 수 있었지만, 제너릭에서 오브젝트로 선언하면 오브젝트만 들어갈 수 있음

⇒ 그래서 특정타입으로 지정해서 사용할 수 있도록 하는것이 제너릭인것 ㅇㅋ?

class Util2{
	public static <T extends Number> int compare(T t1,T t2) {
		double v1 = t1.doubleValue();
		System.out.println(v1);
		double v2 = t2.doubleValue();
		System.out.println(v2);
		return Double.compare(v1, v2);
	}
}

// 제한된 타입 파라미터 (Bounded Parameter) 예제

public class T03_GenericMethodTest {
	public static void main(String[] args) {
		int result1 = Util2.compare(10, 20);
		System.out.println(result1);
		int result2 = Util2.<Number>compare(3.14, 3);
		System.out.println(result2);
	}
	

}

와일드카드

  • 와일드카드는 제너릭 타입을 이용한 타입 안전한 코드를 위해 사용되는 특별한 종류의 인수(Argument)로서, 변수선언, 객체생성 및 메서드 정의할 때 사용된다. (제너릭 타입 선언시에는 사용할 수 없다.)

형식

<? extends T> => 와일드 카드의 상한 제한. T와 그 자손들만 가능
<? super T> => 와일드 카드의 하한 제한. T와 그 조상들만 가능
<?> => 모든타입이 가능 <? extends Object>와 동일

예제

import java.util.ArrayList;
import java.util.List;

public class T04_WildCardTest {
	public static void main(String[] args) {
		FruitBox<Fruit> fruitBox = new FruitBox<Fruit>(); //과일상자
		FruitBox<Apple> appleBox = new FruitBox<Apple>(); // 사과상자
		
		fruitBox.add(new Apple());
		fruitBox.add(new Grape());
		
		appleBox.add(new Apple());
		appleBox.add(new Apple());
		//appleBox.add(new Grape()); //Apple만 타입으로 들어오도록 지정해놨으므로 에러
		
		Juicer.makeJuice(fruitBox);
		// Juicer.makeJuice(appleBox); // <Fruit>타입만 들어 올 수 있도록 했는데 apple타입 왔으니까 오류 => 뭐든 들어오게 하려면?
										// 아래 쥬서의 리턴타입앞에 T를 선언하도록 바꾼다
										// 근데 이젠 굳이 과일이 아니여도 다 들어갈수 있게 돼버림 => 타입제한 extends필요
		
		
	}
	

}

class Juicer {
	static <T extends Fruit> void makeJuice(FruitBox<T> box) {
		String fruitListStr = ""; //과일목록
		
		int cnt = 0;
		for(T f : box.getFruitList()) {
			if(cnt == 0) {fruitListStr += f;
			}else {
				fruitListStr += ","+f;
			}
			cnt++;
		}
		System.out.println(fruitListStr+"=> 쥬스 완성!!!");
}}

class Fruit{
	private String name;

	public Fruit(String name) {
		super();
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "과일("+name+")";
	}
	
}

class Apple extends Fruit {
	public Apple() {
		super("사과");
	}
	
}
class Grape extends Fruit {
	public Grape() {
		super("포도");
	}
	
}

class FruitBox<T> {
	private List<T> fruitList;
	
	public FruitBox() {
		fruitList = new ArrayList<>();
	}
	
	public void add(T fruit) {
		fruitList.add(fruit);
	}

	public List<T> getFruitList() {
		return fruitList;
	}
	
	
	
}

import java.util.ArrayList;
import java.util.List;

/**
 * 상한 및 하한 제한 와일드 카드 예제
 */

public class T05_WildCardTest {
	
	public void printMemberInfo(List<? extends Member> list) {
		/**
		 * extends 키워드를 이용한 상한 제한(Upper Bounds) 예제
		 * list 안의 객체는 반드시 Member타입의 객체임을 보장할 수 있다.
		 */
		for(Member mem : list) {
			System.out.println(mem);
		}
	}
	
	public void printMemberInfo2(List<? super Member> list) {
		/**
		 * super 키워드를 이용한 하한 제한(lower bounds)
		 * Member 타입의 변수를 이용하여 list로부터 객체를 꺼내올 수 없다.
		 */
		for(Member mem : list) {
			System.out.println(mem);
		}
	}
	
	/**
	 * 회원정보 등록
	 * @param list
	 */
	public void registerMemberInfo(List<? extends Member> list) {
		/**
		 * Member 타입의 객체라고 항상 list에 추가할 수 있음을 보장해 주지 않는다.
		 * (Member타입 또는 Member를 상속한 그 어떤 타입을 의미하므로 아직 구체적인
		 * 타입이 정해지지 않았다. => 컴파일 에러 발생.)
		 */
		Member mem = new Member("홍길동", 33);
		list.add(mem); // Member타입 객체 추가 불가
	}
	
	public void registerMemberInfo2(List<? super Member> list) {
		/**
		 * super키워드를 이용한 하한 제한 예제
		 * (list는 Member타입의 객체를 포함한다는 것을 보장 받는다.
		 * => Member타입 또는 Member타입의 슈퍼타입을 담은 리스트를 의미함...)
		 */
		Member mem = new Member("홍길동", 33);
		list.add(mem); // Member타입 객체 추가 가능함.
	}
	
	public static void main(String[] args) {
		T05_WildCardTest wc = new T05_WildCardTest();
		List<Member> memList = new ArrayList<Member>();
		
		wc.registerMemberInfo2(memList);
		wc.printMemberInfo(memList);
	}
	
}

/**
 * 회원정보
 */
class Member {
	private String name;
	private int age;
	public Member(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public String toString() {
		return "Member [name=" + name + ", age=" + age + "]";
	}
}
package kr.or.ddit.basic;

import java.util.ArrayList;
import java.util.List;

public class T06_WildCardTest {
	
	// 장바구니 항목조회를 위한 메서드(모든 항목)
	public static void displayCartItemInfo(Cart<?> cart ) {
		System.out.println(" = 음식류 장바구니 항목 리스트 = ");
		for(Object obj : cart.getList()) {
			System.out.println(obj);
		}
		System.out.println("--------------------------------");
	}
	
	// 장바구니 항목조회를 위한 메서드(음료나 그 하위)
	public static void displayCartItemInfo2(Cart<? extends Drink> cart ) {
		System.out.println(" = 음료류 장바구니 항목 리스트 = ");
		for(Object obj : cart.getList()) {
			System.out.println(obj);
		}
		System.out.println("--------------------------------");
	}
	
	// 장바구니 항목조회를 위한 메서드(고기나 그 상위)
	public static void displayCartItemInfo3(Cart<? super Meat> cart ) {
		System.out.println(" = 고기류외 음식 장바구니 항목 리스트 = ");
		for(Object obj : cart.getList()) {
			System.out.println(obj);
		}
		System.out.println("--------------------------------");
	}
	
	public static void main(String[] args) {
		
		Cart<Food> foodCart = new Cart<Food>();
		foodCart.addItem(new Meat("돼지고기", 5000));
		foodCart.addItem(new Meat("소고기", 10000));
		foodCart.addItem(new Juice("오렌지쥬스", 1500));
		foodCart.addItem(new Coffee("아메리카노", 2000));
		
		Cart<Meat> meatCart = new Cart<>();
		meatCart.addItem(new Meat("돼지고기", 5000));
		meatCart.addItem(new Meat("소고기", 10000));
		
		Cart<Drink> drinkCart = new Cart<>();
		drinkCart.addItem(new Juice("오렌지쥬스", 1500));
		drinkCart.addItem(new Coffee("아메리카노", 2000));
		
		Cart<Coffee> coffeeCart = new Cart<>();
		coffeeCart.addItem(new Coffee("아메리카노", 2000));
		coffeeCart.addItem(new Coffee("에스프레소", 1500));
		coffeeCart.addItem(new Coffee("캬라멜마끼아또", 5000));
		
		displayCartItemInfo(foodCart);
		displayCartItemInfo(meatCart);
		displayCartItemInfo(drinkCart);
		
		//displayCartItemInfo2(foodCart);
		//displayCartItemInfo2(meatCart);
		displayCartItemInfo2(drinkCart);
		displayCartItemInfo2(coffeeCart);
		
		displayCartItemInfo3(foodCart);
		displayCartItemInfo3(meatCart);
		//displayCartItemInfo3(drinkCart);
		
		
		
		
		
	}
}

// 음식
class Food {
	private String name; // 음식이름
	private int price; 	// 가격
	
	public Food(String name, int price) {
		this.name = name;
		this.price = price;
	}
	
	@Override
	public String toString() {
		return this.name + "(" + this.price + "원)";
	}
}

// 음료
class Drink extends Food {
	public Drink(String name, int price) {
		super(name, price);
	}
}
// 쥬스
class Juice extends Drink {
	public Juice(String name, int price) {
		super(name, price);
	}
}
// 커피
class Coffee extends Drink {
	public Coffee(String name, int price) {
		super(name, price);
	}
}

// 고기
class Meat extends Food {
	public Meat(String name, int price) {
		super(name, price);
	}
}

// 장바구니
class Cart<T> {
	private List<T> list = new ArrayList<T>();
	
	public List<T> getList() {
		return this.list;
	}
	
	public void addItem(T item) {
		list.add(item);
	}
}

가변형 인수

  • 가변형 인수 => 메서드의 매개변수의 개수가 실행될 때마다 다를 때 사용한다
  • 가변형 인수는 메서드 안에서는 배열로 처리된다.
  • 가변형 인수는 한가지 자료형만 사용할 수 있다.
package kr.or.ddit.basic;

public class T07_ArgsTest {
	
	static final int LION = 1;
	static final int TIGER = 2;
	static final int FOX = 3;
	static final int 사과 = 1;
	
	// 배열을 이용한 메서드
	// 매개변수로 받은 정수들의 합계를 구하는 메서드(이 정수들의 개수는 상황에 따라 다르다.)
	public int sumArr(int[] data) {
		int sum = 0;
		for(int i=0; i<data.length; i++) {
			sum += data[i];
		}
		return sum;
	}
	
	// 가변형 인수를 이용한 메서드
	public int sumArgs(int...data) {
		int sum = 0;
		for(int i=0; i<data.length; i++) {
			sum += data[i];
		}
		return sum;
	}
	
	// 가변형 인수와 일반적인 인수를 같이 사용할 경우에는
	// 가변형 인수를 제일 뒤에 배치해야 한다.
	public String sumArg2(String name, int...data) {
		int sum = 0;
		for(int i=0; i<data.length; i++) {
			sum += data[i];
		}
		return name + "씨 점수 : " + sum;
	}
	
	public static void main(String[] args) {
		T07_ArgsTest at = new T07_ArgsTest();
		
		int[] nums = {100, 200, 300};
		System.out.println(at.sumArr(nums));
		System.out.println(at.sumArr(new int[]{1,2,3,4,5}));
		System.out.println();
		
		System.out.println(at.sumArgs(100, 200, 300));
		System.out.println(at.sumArgs(1, 2, 3 , 4, 5));
		System.out.println();
		
		System.out.println(at.sumArg2("홍길동", 1,2,3,4,5,6,7,8,9));
		
		if(LION == 사과) {
			System.out.println("맞습니다.");
		}else {
			System.out.println("다릅니다.");
			
		}
		
		
	}
}

Enum

  • 열거형 => 상수값들을 선언하는 방법
    static final int A = 0;
    static final int B = 1;
    static final int C = 2;
    static final int D = 3;
    
    enum Data {A, B, C, D};

	열거형 선언하는 방법
   =>  enum 열거형이름 {상수값1, 상수값2, ..., 상수값n};
package kr.or.ddit.basic;

public class T08_EnumTest {
	
	// City 열거형 객체 선언 (기본값을 이용하는 열거형)
	public enum City {서울, 부산, 대구, 광주, 대전};
	
	public enum HomeTown{광주, 대구, 부산, 목포, 대전};
	
	// 데이터값을 임의로 지정한 열거형 객체 선언
	// 데이터값을 정해 줄 경우에는 생성자를 만들어서 괄호속의 값이 변수에 저장되도록 해야 한다.
	public enum Season {("3월부터 5월까지"), 여름("6월부터 8월까지"), 
		가을("9월부터 11월까지"), 겨울("12월부터 2월까지");
		
		// 괄호속의 값이 저장될 변수 선언
		private String str;
		
		// 생성자 만들기(열거형의 생성자는 제어자가 묵시적으로 'private'이다.)
		Season(String data) {
			this.str = data;
		}
		
		// 값을 반환하는 메서드 작성
		public String getStr() {
			return str;
		}
	}
	
	public static void main(String[] args) {
	/*
	   열거형에서 사용되는 메서드
	   
	 1. name() => 열거형 상수의 이름을 문자열로 반환한다.
	 2. ordinal() => 열거형 상수가 정의된 순서값을 반환한다.(기본적으로 0부터 시작)
	 3. valueOf("열거형상수이름"); => 지정된 열거형에서 '열거형상수이름'과 일치하는
	                            열거형 상수를 반환한다.
	*/
		City myCity1; // 열거형 객체변수 선언
		City myCity2;
		
		// 열거형 객체변수에 값 저장하기
		myCity1 = City.서울;
		myCity2 = City.valueOf("서울");
		
		System.out.println("myCity1 : " + myCity1.name());
		System.out.println("myCity1의 ordinal : " + myCity1.ordinal());
		System.out.println();
		
		System.out.println("myCity2 : " + myCity2.toString());
		System.out.println("myCity2의 ordinal : " + myCity2.ordinal());
		System.out.println("=======================================");
		
		Season ss = Season.valueOf("여름");  // Season ss = Season.여름;
		System.out.println("name => " + ss.name());
		System.out.println("ordinal => " + ss.ordinal());
		System.out.println("get메서드 => " + ss.getStr());
		System.out.println("----------------------------------------");
		
		// 열거형이름.values() => 데이터를 배열로 가져온다.
		Season[] enumArr = Season.values();
		for(int i=0; i<enumArr.length; i++) {
			System.out.println(enumArr[i].name() + " : " 
						+ enumArr[i].getStr());
		}
		System.out.println();
		
		for(City city : City.values()) {
			System.out.println(city + " : " + city.ordinal());
		}
		
		City city = City.대구;
		
		System.out.println(city == City.대전);
		System.out.println(city == City.대구);
		//System.out.println(city == HomeTown.대구);
		
		System.out.println("대구 => " + city.compareTo(City.대구));
		System.out.println("서울 => " + city.compareTo(City.서울));
		System.out.println("대전 => " + city.compareTo(City.대전));
	}
	
	
	
	
	
}

Java Reflection

  1. 리플렉션은 런타임 시점에 클래스 또는 멤버변수, 메서드, 생성자에 대한 정보를 가져오거나 수정할 수 있고, 새로운 객체를 생성하거나 메소드를 실행 할 수 있다
    (컴파일 시점에 해당 정보를 알 수 없는 경우(소스코드 부재시)에 유용하게 사용 될 수 있음)
  2. Reflection API는 java.lang.reflect패키지와 java.lang.Class를 통해 제공된다.
  3. java.lang.Class의 주요 메소드
    • getName(), getSuperClass(), getInterfaces(), getModifiers() 등
  4. java.lang.reflect 패키지의 주요클래스
    • Field, Method, Constructor, Modifier 등

public class T01_ClassObjectCreationTest {
	public static void main(String[] args) throws ClassNotFoundException {
		
		// 첫번째방법 : Class.forName() 이용하기
		Class<?> klass = Class.forName("kr.or.ddit.reflection.T01_ClassObjectCreationTest");
		
		
		// 두번째방법: getClass() 메서드 이용하기
		T01_ClassObjectCreationTest obj = new T01_ClassObjectCreationTest();
		klass = obj.getClass();
		
		// 세번째방법: .class
		klass = T01_ClassObjectCreationTest.class;
		
		
		
	}

}
import kr.or.ddit.basic.PrintAnnotation;

public class SampleVO implements Comparable<SampleVO> {
	
	public String id;	 
	protected String name;
	private int age;
	
	public SampleVO(String id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}
	
	public SampleVO() throws RuntimeException {
		
	}
	
	@PrintAnnotation(value = "$", count = 30)
	public String getId() throws RuntimeException {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "SampleVO [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

	@Override
	public int compareTo(SampleVO o) {
		
		return name.compareTo(o.getName());
	}
	
	
}
import java.lang.reflect.Modifier;

/**
 * Class의 메타데이터 가져오기 예제
 * @author sem
 *
 */
public class T02_ClassMetadataTest {
	public static void main(String[] args) {
		
		// 클래스 오브젝트 생성하기
		Class<?> clazz = SampleVO.class;
		
		System.out.println("심플 클래스명 : " + clazz.getSimpleName());
		System.out.println("클래스명 : " + clazz.getName());
		System.out.println("상위 클래스명 : " + clazz.getSuperclass().getName());
		
		// 패키지 정보 가져오기
		Package pkg = clazz.getPackage();
		System.out.println("패키지 정보 : " + pkg.getName());
		
		// 해당 클래스에서 구현하고 있는 인터페이스 목록 가져오기
		Class<?>[] interfaces = clazz.getInterfaces();
		
		System.out.print("인터페이스 목록 =>");
		for(Class<?> inf : interfaces) {
			System.out.print(inf.getName() + " | ");
		}
		System.out.println();
		
		// 클래스의 접근 제어자 가져오기(flag bit 값 반환됨. => 접근제어자의 유무 체크함)
		int modFlag = clazz.getModifiers();
		
		System.out.println("접근 제어자 : " + Modifier.toString(modFlag));
	}
}

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 클래스에 선언된 메서드의 메타정보 가져오기
 * @author sem
 *
 */
public class T03_MethodMetadataTest {
	public static void main(String[] args) throws ClassNotFoundException {
		// 클래스 오브젝트 가져오기
		Class<?> klass = Class.forName("kr.or.ddit.reflection.SampleVO");
		
		// 클래스에 선언된 모든 메서드의 메타데이터 정보 가져오기
		Method[] methodArr = klass.getDeclaredMethods();
		
		for(Method m : methodArr) {
			
			System.out.println("메서드명 : " + m.getName());
			System.out.println("메서드 리턴타입 : " + m.getReturnType());
			
			// 해당 메서드의 접근제어자 정보 가져오기
			int modFlag = m.getModifiers();
			System.out.println("메서드 접근제어자 : " + Modifier.toString(modFlag));
			
			// 해당 메서드의 파라미터 타입 가져오기
			Class<?>[] paramArr = m.getParameterTypes();
			System.out.print("메서드 파라미터 타입 : ");
			for(Class<?> clazz : paramArr) {
				System.out.print(clazz.getName() + " | ");
			}
			System.out.println();
			
			// 해당 메서드에서 던지는 예외타입 가져오기
			Class<?>[] exTypeArr = m.getExceptionTypes();
			System.out.print("메서드에서 던지는 예외타입 목록 : ");
			for(Class<?> clazz : exTypeArr) {
				System.out.print(clazz.getName() + " | ");
			}
			System.out.println();
			
			// 해당 메서드에 존재하는 Annotation 타입 정보 가져오기
			Annotation[] annos = m.getDeclaredAnnotations();
			System.out.println("Annotation 타입 : ");
			for(Annotation anno : annos) {
				System.out.print(anno.annotationType().getName() + " | ");
			}
			
			System.out.println();
			System.out.println("---------------------------------------------");
			
		}
		
	}
}


A

Annotation

  • 프로그램 소스코드 안에 다른 프로그램을 위한 정보를 미리 약속된 형식으로 포함시킨 것.
  • 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서도 다른 프로그램에게 유용한 정보를 제공한다.
  • 어노테이션이 붙은 코드는 어노테이션에 구현된 정보에 따라 연결되는 방향이 결정된다. 즉 전체 소스코드의 비지니스 로직엔 영향을 주지 않지만 해당 타겟의 연결방법이나 소스코드의 구조를 변경 할 수 있다. 어떤 용도로 사용할지, 어떤 역할을 줄지 결정하는것.

종류

  • 표준 애너테이션
  • 메타 애너테이션(애너테이션을 위한 애너테이션, 즉 애너테이션을 정의할 때 사용하는 애너테이션)

형식

@interface 애너테이션이름 {
	요소타입   타입요소이름(); // 반환값이 있고 매개변수는 없는 추상메서드의 형태
...
}

어노테이션 요소의 규칙

  1. 요소의 타입은 기본형, String, enum, annotation, class만 허용된다.
  2. ()안에 매개변수를 선언할 수 없다.
  3. 예외를 선언할 수 없다.
  4. 요소의 타입에 타입 매개변수(제네릭타입문자)를 사용할 수 없다.

예제

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)	// 적용가능한 대상 지정
@Retention(RetentionPolicy.RUNTIME)	// 애너테이션 유지 기간 지정(CLASS가 기본값임.)
public @interface PrintAnnotation {
	String value() default "-"; // 기본값을 "-"로 지정
	int count() default 20;		// 기본값을 20으로 지정
}
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AnnotationTest {
	public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
		
		// 접근할 클래스로 만든 객체 생성
		Service service = new Service();
		
		// 클래스 정보를 담은 클래스 객체 생성
		Class<?> klass = service.getClass();
		
		// 리플렉션을 이용한 메서드 정보 접근하기
		Method[] declaredMethods = klass.getDeclaredMethods();
		
		for(Method m : declaredMethods) {
			
			System.out.print(m.getName()); // 메서드명 출력
			
			Annotation[] annos = m.getAnnotations();
			
			for(Annotation anno : annos) {
				if(anno.annotationType().getSimpleName()
						.equals("PrintAnnotation")) {
					PrintAnnotation printAnn = (PrintAnnotation) anno;
					for(int i=0; i<printAnn.count(); i++) {
						System.out.print(printAnn.value());
					}
				}
			}
			
			System.out.println(); // 줄바꿈 처리
			
			//m.invoke(service);
			
			Class<?> clazz = Service.class;
			
			service = (Service)clazz.newInstance();
			
			m.invoke(service);
		}
	}
}
public class Service {
	
	@PrintAnnotation
	public void method1() {
		System.out.println("메서드1에서 출력되었습니다.");
	}
	
	@PrintAnnotation("%")
	public void method2() {
		System.out.println("메서드2에서 출력되었습니다.");
	}
	
	@PrintAnnotation(value = "#", count = 25)
	public void method3() {
		System.out.println("메서드3에서 출력되었습니다.");
	}
}

0개의 댓글