(형식)
class 클래스명<제너릭타입글자> {
제너릭타입글자 변수명; // 변수선언에 제너릭을 사용할경우
...
제너릭타입글자 메서드명() {
...
return 값;
}
...
}
T => Type
K => Key
V => Value
E => Element(자료구조에 들어가는 것들을 나타낼 때 사용)
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);
}
}
+) 제너릭 인터페이스는 클래스와 비슷하게 작동한다
리턴타입 앞에 <>기호를 추가하고 타입 파라미터를 기술 후 사용함
public <T> T genericMethod(T o) { // 제네릭 메소드
...
}
[접근 제어자] <제네릭타입> [반환타입] [메소드명]([제네릭타입] [파라미터]) {
// 텍스트
}
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에 제네릭 넣기!
}
}
제너릭에서 오브젝트는 타입임! 평소에 오브젝트는 최상위타입이므로 어떤타입이든 다 들어 갈 수 있었지만, 제너릭에서 오브젝트로 선언하면 오브젝트만 들어갈 수 있음
⇒ 그래서 특정타입으로 지정해서 사용할 수 있도록 하는것이 제너릭인것 ㅇㅋ?
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);
}
}
<? 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("다릅니다.");
}
}
}
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.대전));
}
}
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
@interface 애너테이션이름 {
요소타입 타입요소이름(); // 반환값이 있고 매개변수는 없는 추상메서드의 형태
...
}
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에서 출력되었습니다.");
}
}