[ITWILL : JAVA]기본클래스 이외에 자바 구성요소3 : annotation, generic

### ITWILL학원 : 18강 JAVA BY 윤미영강사

annotation(애노테이션)과 generic(제네릭)에 대해 정리해봤다.

1 annotation 애노테이션

  • 사전적 의미는 주석이다
  • 애노테이션은 개발자가 읽기 위한 주석이 아니라 컴파일러등에게 전달하는 일종의 메타데이터이다.
  • 메타데이터란 : 다른 데이터를 설명해주는 데이터로 속성 정보라고도 한다.
  • 함수형인터페이스란 : 인터페이스 중에서 한 개의 abstract메서드를 가진 인터페이스.
    • 함수형인터페이스 중 익명클래스를 사용했다면 람다식사용가능
애노테이션 설명
@Override 컴파일러에게 재정의된 메서드라고 알려줌
@Deprecated 앞으로 없어질 수 있으니 사용을 자제하라고 알려줌 ex)보안적인 이슈가 발생했거나 더 좋은 성능의 메서드로 대체를 권장할 때 사용함
@SupressWarnings(“all/unused”) 컴파일러에게 특정 경고 메시지를 무시하라고 알려줌 ex)경고내용에 이미 알고 있으니 컴파일러가 보여주지 않아도 될때 사용함
@Functionallnterface 함수형 인터페이스라는 것을 알려줌




2 Generic

  • 제네릭의 사전적 의미 : 일반화
  • 장점 : 컴파일 시점에서 사용할 수 있는 객체타입을 체크함 -> 프로그램의 안정성이 획기적으로 향상되고 코드도 줄어듬.
  • 타입 파라미터를 사용하면 클래스에 넘겨줄 수 있는 데이터타입을 제한할 수 있다.
1
2
3
4
5
6
// 꺽쇠 전체<T>를 제네릭이라고 부른다.
//그 안의 파라미터 T를 타입파라미터라고 부른다.

class test<T>{

}
  • 사용처 : 데이터 저장 구조
  • 사용 주의점
    • 타입 파라미터는 인스턴스변수임 -> static멤버에는 타입파라미터 적용못함.
    • 타입 파라미터를 이용해서 객체생성 못함 why? new 연산자는 컴파일 타입에 정확한 타입을 알아야하는데 클래스 내부에서 파라미터만 보아선 타입을 알 길이 없기때문에.
    • 타입 파라미터에서 사용가능한 메서드는 Object에 선언된 것으로 한정한다 why? 아직 타입이 정해지지 않은 상태에서 누구의 메서드를 가져다 쓸 수 없기때문에 최상위클래스인 Object의 메서드들만 사용가능.




2-1 사용 가능한 파라미터 타입의 제한

  1. 가변인자 : 파라미터의 갯수를 모를때 가변인자를 사용할 수 있다
  2. 가변인자 파라미터 타입의 제한 제네릭 예시 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class NumberBox<T extends Number>{
public void addSomes(T ... ts){ //가변인자
double d =0;
for (T t: ts){
//d = t+d;
//t가 뭔지 모르기때문에 double과 더할수가없다.
//따라서 t를 double로 바꿔줌.
//Number클래스의 doubleValue()메서드를 사용하면 된다.
d = t.doubleValue() + d;
}
System.out.println("총 합은 : "+ d);
}
}

public class ExtndsTest {

public static void main(String[] args) {
NumberBox<Number> numBox = new NumberBox<>();
numBox.addSomes(1.5, 5, 4L);

NumberBox<Integer> intBox = new NumberBox<>();
intBox.addSomes(1,2,3);

//Number의 하위 클래스만 사용 가능.
//NumberBox<String> strBox = new NumberBox<>();
}
}
  1. 메서드 오버로딩과 가변인자의 차이점은 무엇일까?

    • 메서드 오버로딩 : 다양한 데이터 타입 받을 수 있음
    • 가변인자 : 데이터타입 한 종류만 받을 수 있음
  2. 메서드 오버로딩 예시 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
OverloadConstructorPerson(String name, int age, boolean isHungry){
this.name = name;
this.age = age;
this.isHugry = false;
}

OverloadConstructorPerson(String name, int age){
//this()용법사용
this(name, age, false);
}

OverloadConstructorPerson(String name){
//this()용법사용
this(name, 0, false);
}

OverloadConstructorPerson(){
//아무 코드도 없으면 기존 멤버변수값이 출력된다.
}




2-2 메서드에 선언된 제네릭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class TypeParam<T> {

T some; //데이터타입 : T, 변수명 = some
T some1; //데이터타입 : T, 변수명 = some1

public TypeParam(T some, T s1){ //생성자 (파라미터2개 : 파라미터의 데이터타입은 T이다)
this.some = some; //멤벼변수와 파라미터 이름이 같아서 this를 사용
some1 = s1; //멤버변수와 파라미터 이름 달리해서 this생략가능
}

public <P> void method1(P p){ //파라미터 데이터 타입이 p
System.out.println("클래스 레벨의 T : "+some.getClass().getName());
System.out.println("파라미터 : "+p.getClass().getName());
}

public <P> P method2(P p){ //리턴값의 데이터타입 p인 메서드
System.out.println("클래스 레벨의 T : "+some.getClass().getName());
System.out.println("파라미터 : "+ p.getClass().getName());
return p;
}
public static void main(String[] args) {
//데이터타입 T -> String으로 지정 후 객체생성
TypeParam<String> tpmt = new TypeParam<>("멤버변수some값설정", "멤버변수some1값설정"); //some에다가 파람값 넣어줌

//멤버변수출력
System.out.println(tpmt.some);
System.out.println(tpmt.some1);

//파라미터 타입 명시하지않음 : 컴파일러가 매개변수의 값을 보고 파라미터 p의 데이터 타입을 결정
tpmt.method1(10);
tpmt.method2(10);

//파라미터 타입 직접 지정 : 파라미터타입 p -> Long으로 지정
tpmt.<Long>method2(20L);
}

}

만약 객체생성시 TypeParam<String> tpmt = new TypeParam<>()하고싶다면 어떻게하면될까?
생성자 오버로딩을 하면된다.

1
2
3
4
//생성자오버로딩
public TypeParam(){ //생성자 (
}
TypeParam<String> tpmt = new TypeParam<>();

에러가 뜨지않고 객체생성은 잘되지만 멤버변수의 데이터타입이 제네릭이기때문에 멤버변수에 값을 넣을수도없고 파라미터도 없기때문에 현 코드에서는 쓸수있는 방법이 없다.




2-3 와일드카드의 사용

  • 제네릭 메서드에서는 사용되는 타입 파라미터에 제한을 두기 위해 와일드카드로 ‘?’를 사용
  • 단독사용 가능
  • extends나 super와 함께 사용가능.
기호 설명
<?> 타입의 제한이 없으며 <? extends Object>와 동일
<? extends T> 와일드카드의 상한을 제한. 이 타입 파라미터에는 T와 T를 상속받은 자손만 대입할 수 있다.
<? super T> 와일드카드의 하한을 제한. 이 타입 파라미터에는 T와 그 조상타입만 대입할 수 있다.
  • 예시 코드
    • getClass() : 객체가 속하는 클래스의 정보를 알아내는 메소드이다.
    • getName() : 클래스의 이름을 리턴하는 메소드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Person{};
class SpiderMan extends Person{};
class PersonBox<T>{};

public class wildTypeTest { //일반클래스

//제네릭메서드
public void method1(PersonBox<?> some){}
//제네릭메서드 + 일반클래스상속
public void method2(PersonBox<? extends Person> some){}
//제네릭메서드 + Person의 조상인 Object클래스 상속
public void method3(PersonBox<? super Person> some){}

public static void main(String[] args) {
wildTypeTest wtt = new wildTypeTest(); //객체생성

//메서드1의 파라미터는 사실상 제약이 없다.
wtt.method1(new PersonBox<Object>());
wtt.method1(new PersonBox<Person>());
wtt.method1(new PersonBox<SpiderMan>());

//메서드2의 파라미터는 Person또는 Person을 상속받은 클래스를 타입 파라미터로 받을 수 있다.
//wtt.method2(new PersonBox<Object>());
wtt.method2(new PersonBox<Person>());
wtt.method2(new PersonBox<SpiderMan>());

//메서드3의 파라미터는 Person또는 Person조상만 가능.
wtt.method3(new PersonBox<Object>());
wtt.method3(new PersonBox<Person>());
//wtt.method3(new PersonBox<SpiderMan>());
}

}