
메서드 선언부에 지네릭 타입이 선언된 메서드 (반환 타입 앞)
ex) Collections.sort()
static <T> void sort(List<T> list, Comparator<? super T> c)
static멤버에는 타입 매개변수를 사용 할 수 없지만, 메서드에 지네릭 타입은 가능!
-> 지역변수와 같다고 생각하자!
class FruitBox<T>
{
...
static <T> void sort(List<T> list, Comparator<? super T> c){
...
}
}
앞에 < T >쓰는 이유 예시
public static ArrayList<? extends Product> merge(
ArrayList<? extends Product> list, ArrayList<? extends Product> list2)
{
ArrayList<? extends product> newList = new ArrayList<>(list);
newList.addAll(list2);
return newList
}
/* 둘은 같은 코드 즉 ArrayList의 제네릭 타입을 제한해주고 코드를
간결히 해준다고 생각하자! */
public static <T extends Product> ArrayList<T> merge(ArrayList<T> list, ArrayList<T> list2)
{
ArrayList<T> newList = new ArrayList<>(list);
newList.add
}
static <T extends Fruit> Juice makeJuice(FruitBox<T> box)
{
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> fruitBox = new FruitBox<Apple>();
...
// 원래라면 Juicer.<Fruit>makeJuice(fruitBox)
// 컴파일러가 대입된 타입을 추정할 수 있기에 생략 가능
System.out.println(Juicer.makeJuice(fruitBox));
System.out.println(Juicer.makeJuice(appleBox));
// 대입된 타입 생략할 수 없을 땐 참조 변수나 클래스 이름 생략 불가
System.out.println(<Fruit>makeJuice(fruitBox)); // 에러. 클래스이름 생략 불가
System.out.println(this.<Fruit>makeJuice(fruitBox));
System.out.println(Juicer.<Fruit>makeJuice(fruitBox));
지네릭 타입과 원시 타입 간의 형변환
Box box = null;
Box<Object> objBox = null;
// 가능하지만 경고 발생
box = (Box)objBox; 지네릭 -> 원시
objBox = (Box<Object>)box; 원시 -> 지네릭
Box<Object> objBox = null;
Box<String> strBox = null;
// 에러 발생
objBox = (Box<Object>)strBox;
strBox = (Box<String>)objBox;
//이유
Box<Object> objBox = (Box<Object>)new Box<String>(); // 에러, 형변환 불가
// Box<String> -> Box<? extends Object> 형 변환 가능
Box<? extends Object> wBox = new Box<String>();
컴파일러는 지네릭 타입을 이용해서 소스파일 체크, 필요한 곳에 형변환 넣어준 뒤 지네릭 타입 제거
/* 지네릭 타입이 만약 < T extends Fruit > 라면 T는 Fruit으로 치환됨
< T >의 경우 -> Object */
class Box<T extends Fruit> {
void add(T t){
...
}
}
change
class Box{
void add(Fruit t){
...
}
}
T get(int i){
return list.get(i)
}
change
Fruit get(int i){
return (Fruit)list.get(i);
}
여러 상수를 선언해야 할 때 편리하게 선언하는 방법
class Card{
// 상수가 많을 시 불필요한 카드가 너무 많음
static final int CLOVER = 0;
static final int HEART = 1;
static final int DIAMOND = 2;
static final int SPADE = 3;
static final int TWO = 0;
static final int THREE = 1;
static final int FOUR = 2;
final int kind;
final int num;
}
// 0 부터 시작하는 정수값이 자동으로 할당
class Card{ // 0, 1, 2, 3
enum Kind { CLOVER, HEART, DIAMOND, SPADE} //열거형 Kind 정의
enum Value { TWO, THREE, FOUR } //열거형 Value 정의
final Kind kind; // 타입이 int가 아닌 Kind
final Value value;
}
if(Card.CLOVER == Card.Two) // true 이지만 false가 의미상 맞음
// 열거형을 이용해서 상수 정의한 경우 값 비교 전 타입을 먼저 비교하므로 에러 발생
if(Card.Kind.CLOVER == Card.Value.TWO) //컴파일 에러
enum 열거체 이름{ 상수명1, 상수명 2, ...}
enum Direction { EAST, SOUTH, WEST, NORTH}
Class Unit{
int x, y;
Direction dir; // 열거형 인스턴스 변수 선언
void int(){
dir = Direction.EAST; // EAST로 초기화
}
}
/* 열거형 상수 간 비교시 == 사용 가능, 반면 <, > 같은 연산은 불가하지만 compareTo는
사용 가능 */
if(dir==Direction.EAST){
x++;
} else if (dir > Direcion.West){ // 에러
....
} else if(dir.compareTo(Direction.WEST) > 0){
...
}
모든 열거형의 조상은 java.lang.Enum -> 여러 메서드 제공
// 열거형의 Class객체를 반환
Class<E> getDeclaringClass()
// 열거형 상수의 이름을 문자열로 반환
String name()
// 열거형 상수가 정의된 순서를 반환(0부터 시작)
int ordinal()
// 지정한 열거형에서 name과 일치하는 열거형 상수 반환
T valueOf(Class<T> enumType, String name)
// 열거형에 정의된 모든 상수 출력
static E[] values()
// 열거형 상수의 이름으로 문자열 상수에 대한 참조를 얻을 수 있게 해줌
static E valueOf(String name)
package ch12;
enum Direction{ EAST, SOUTH, WEST, NORTH}
public class Ex12_5 {
public static void main(String[] args) {
Direction d1 = Direction.EAST;
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");
System.out.println("d1="+d1);
System.out.println("d2="+d2);
System.out.println("d3="+d3);
System.out.println("d1==d2 ?" + (d1==d2));
System.out.println("d1==d3 ?" + (d1== d3));
System.out.println("d1.equals(d3)" + d1.equals(d3));
// System.out.println("d2 > d3? " + (d1 > d3)); //에러
System.out.println("d1.compareTo(d3) ?" +(d1.compareTo(d3)));
System.out.println("d1.compareTo(d2) ?" +(d1.compareTo(d2)));
switch(d1) {
case EAST:
System.out.println("The direction is East."); break;
case SOUTH:
System.out.println("The direction is East."); break;
case WEST:
System.out.println("The direction is East."); break;
case NORTH:
System.out.println("The direction is East."); break;
default:
System.out.println("Invalid direction. "); break;
}
Direction[] dArr = Direction.values();
for(Direction d : dArr) // for (Direction d: Direction.values())
System.out.printf("%s=%d%n", d.name(), d.ordinal());
}
}
d1=EAST
d2=WEST
d3=EAST
d1==d2 ?false
d1==d3 ?true
d1.equals(d3)true
d1.compareTo(d3) ?0
d1.compareTo(d2) ?-2
The direction is East.
EAST=0
SOUTH=1
WEST=2
NORTH=3
열거형 상수의 값이 불규칙한 경우
enum Direction{
EAST(1), SOUTH(5), WEST(-1), NORTH(10); // 끝에 세미콜론 잊지 말자
//멤버 추가
private final int value; // 정수를 저장할 iv 추가
Direction(int value) { this.value = value;} // 생성자 추가
public int getValue() { return value;}
}
Direction d = new Direction(1) // 에러. 열거형의 생성자는 외부에서 호출 불가
// 열거형의 생성자는 묵시적으로 private
enum Direction{
...
Direction(int value) {
// private Direction(int value)와 동일
...
}
package ch12;
enum Direction2
{
EAST(1, ">"), SOUTH(2, "V"), WEST(3, "<"), NORTH(4,"^");
private static final Direction2[] DIR_ARR = Direction2.values();
private final int value;
private final String symbol;
Direction2(int value, String symbol)
{ // private 있다는 것 생각하기
this.value = value;
this.symbol = symbol;
}
public int getValue() { return value; }
public String getSymbol() { return symbol;}
public static Direction2 of(int dir)
{
if(dir < 1 || dir > 4)
throw new IllegalArgumentException("Invalied value : " + dir);
return DIR_ARR[dir - 1];
}
// 방향 회전 메서드, num의 값만큼 90도씩 시계방향으로 회전
public Direction2 rotate(int num)
{
num = num % 4;
if(num < 0) num+= 4; // num이 음수일때는 시계반대 방향으로 회전
return DIR_ARR[(value-1+num) % 4];
}
public String toString() {
return name() + getSymbol();
}
}
public class Ex12_6 {
public static void main(String[] args) {
for(Direction2 d: Direction2.values())
System.out.printf("%s=%d%n", d.name(), d.getValue());
Direction2 d1 = Direction2.EAST;
Direction2 d2 = Direction2.of(1);
System.out.printf("d1=%s, %d%n", d1.name(), d1.getValue());
System.out.printf("d2=%s, %d%n", d2.name(), d2.getValue());
System.out.println(Direction2.EAST.rotate(1));
System.out.println(Direction2.EAST.rotate(2));
System.out.println(Direction2.EAST.rotate(-1));
System.out.println(Direction2.EAST.rotate(-2));
}
}
과거에는 소스코드와 문서를 따로 관리하였는데 서로 간의 불일치가 발생하였는데 이를 해결하기 위해 소스코드와 문서를 하나로 합치는 javadoc.exe를 만들었다.
/**로 시작하는 주석 안에 소스코드에 대한 설명들이 있고 @이 붙은 태그가 있는데 이를 애너테이션이라고 한다.
애너테이션은 주석처럼 프로그래밍 언어에 영향을 미치지 않으면서 컴파일러에게 유용한 정보를 제공한다.
@Test // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알린다.
public void method(){
...
}
모든 프로그램에게 의미가 있는 것은 아니고, 해당 프로그램에 미리 정의된 종류와 형식으로 작성해야 의미가 있다!
@Override메서드를 오버라이딩 하는 것이라고 컴파일러에게 알려주는 역할class Child extends Parent {
@Override
void parentMethod()
}
@Deprecated사용하지 않을 것을 권장하는 대상에 붙인다.기존의 것들을 함부로 삭제하지 않고 @Duprecated를 사용하는데 이를 사용할 경우 컴파일 시 경고 메시지가 출력된다.class NewClass {
@Deprecated
int oldField;
@Deprecated
int getOldField(){
return oldField;
}
}
@FunctionalInterface// 함수형 인터페이스는 추상 메서드가 하나여야 한다!!!!!!
public interface Runnable {
public abstract void run();
}
@SuppressWarnings경고메시지가 나타나지 않게 해준다.deprecation : @Deprecated가 붙은 대상을 사용해서 발생하는 경고unchecked : 지네릭스로 타입을 지정하지 않았을 때 발생하는 경고rawtypes : 지네릭스를 사용하지 않아서 발생하는 경고varargs : 가변인자의 타입이 지네릭 타입일 때 발생하는 경고 억제@SuppressWarnings("unchecked")
ArrayList list = new ArrayList(); // 지네릭 타입 지정 X
list.add(obj); // 여기서 경고 발생하지만 억제됨!
여러 개 억제도 가능하다!
애너테이션의 애너테이션
새로운 애너테이션을 정의할 때 애너테이션의 적용대상이나 유지기간 등을 지정하는데 사용된다(java.lang.annotation에 포함)
@Target적용가능한 대상을 지정하는데 사용
위 표의 값들은 java.lang.annotation.ElementType 이라는 열겨형에 정의
// SuppressWarnings 애너테이션의 실제 코드
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings{
String[] value();
}
import static java.lang.annotation.ElementType.*;
@Target({FIELD, TYPE, TYPE_USE})
public @interface MyAnnotation { }
@MyAnnotation // 적용 대상이 TYPE인 경우(클래스)
class MyClass{
@MyAnnotation // 적용 대상이 FIELD인 경우(멤버 변수)
int i;
@Myannotation // 적용 대상이 TYPE_USE인 경우
MyClass mc;
}
@Retention애너테이션이 유지되는 기간을 지정하는데 사용

/*
@Override 나 @SuppressWarnings처럼 컴파일러가 사용하는 애너테이션은
유지 정책이 SOURCE -> 컴파일러를 직접 작성할 것이 아니면, 유지 정책 필요 X
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override{}
/*
@FunctionalInterface 는 Override 처럼 컴파일러가 체크하지만, 실행 시에도
사용 되므로 유지 정책이 RUNTIME
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface{}
@Documentedjavadoc으로 작성된 문서에 포함되도록 한다@Inherited상속되도록 한다.@inherited
#interface SuperAnno{}
@SuperAnno
class Parent{}
// @SuperAnno가 붙은 것과 마찬가지
class Child extends Parent{}
@Repeatable여러 번 붙일 수 있게 해준다@Repeatble(ToDos.class) // ToDO애너테이션을 여러 번 반복해서 쓸수 있게 해줌
@ interface ToDO{
String value();
}
// 밑에 처럼 가능 하다는 것
@ToDo("delete test codes.")
@ToDo("override inherited methods")
class MyClass{
...
}
/* 같은 이름의 애너테이션이 하나의 대상에 여러번 적용 될 수 있기에,
하나로 묶어서 다룰 수 있는 애너테이션도 추가로 정의해야 한다 */
@interface ToDos{ // 여러 개의 ToDo애너테이션을 담을 컨테이너 애너테이션 ToDos
Todo[] value(); // 이름이 반드시 value여야 함
}
@interface 애너테이션이름{
타입.요소이름(); // 애너테이션 요소 선언
...
}
@Override에서 그 자체는 애너테이션이고 Override는 애너테이션 타입이다
애너테이션 내에 선언된 메서드를 요소라고 부른다
@interface TestInfo{
// 애너테이션 요소는 반환값이 있고, 매개변수는 없는 추상 메서드 형태
// 요소 값을 빠짐없이 지정해주어야함
int count();
String testedBy();
String[] testTools();
TestType testType();
DateTime testDate();
}
@interface DateTime
{
String yymmdd();
String hhmmss();
}
//지정 예시
@TestInfo{
count = 3, testedBy = "kim",
testTools = {"JUnit", "Autotester"},
testType = TestType.FIRST,
testDAte = @DateTime(yymmdd="160101", hhmmss="235959")
}
public class NewClass{...}
default를 통해 기본 값 지정 가능
애너테이션 요소가 오직 value 인 경우, 적용 시 요소의 이름을 생략하고 값만 적어도 됨
-> 그래서 애너테이션 괄호 안에 바로 { } 배열 값들만 넣어도 되는 것!
괄호로 여러 개의 값 및 기본값 지정 가능!
Annotation
-> 명시적으로 조상 지정 (extends)불가, 일반적인 인터페이스로 정의 되어있음
package java.lang.annotation;
public interface Annotation { // Anotation 자신은 인터페이스이다.
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType(); // 애너테이션 타입을 반환
// 여기에 4개의 추상 메서드는 모든 애너테이션이 물려받음
// 추상메서드지만 구현 안해주어도 사용할 수 있다. -> 컴파일러가 자동으로 구현
}
요소가 하나도 정의되지 않은 애너테이션
명칭정도만 있다
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override{} // 마커 애너테이션. 정의된 요소가 없다
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Test{} // 마커 애너테이션. 정의된 요소가 없다
//즉 @Override, @Test 처럼 앞에 붙여 사용시 적어주는 요소가 없다!!!
요소의 타입은 기본형, String, enum, 애너테이션,Class(설계도 객체) 만 가능하다!
() 안에 매개변수 선언 불가
예외를 선언할 수 없다.
요소를 타입 매개변수로 정의할 수 없다.
@interface AnnoTest
{
int id = 100; // OK. 디폴트 메서드 불가. 인터페이스처럼 상수선언
// static final int id = 100;
String major(int i, int j); // 에러 매개변수 X
String minor() throws Exception; // 예외 선언 불가
ArrayList<T> list(); // 타입 매개변수 사용불가
}
package ch12;
import java.lang.annotation.*;
@Deprecated
@SuppressWarnings("1111") // 유효하지 않은 애너테이션은 무시
// 디폴트 값은 안적어도됨!
@TestInfo(testedBy = "aaa", testDate=@DateTime(yymmdd = "160101", hhmmss = "235959"))
public class Ex12_8 {
public static void main(String[] args) {
// Ex12_8의 Class 객체를 얻는다
Class<Ex12_8> cls = Ex12_8.class;
TestInfo anno = cls.getAnnotation(TestInfo.class);
System.out.println("anno.testedBy()="+anno.testedBy());
System.out.println("anno.testDAte().yymmdd()=" + anno.testDate().yymmdd());
System.out.println("anno.testDate().hhmmss()=" + anno.testDate().hhmmss());
//testTools 읽어오기.
for(String str : anno.testTools())
System.out.println("testTools =" + str);
System.out.println();
// Ex12_8에 적용된 모든 애너테이션을 가져온다
Annotation[] annoArr = cls.getAnnotations();
for(Annotation a : annoArr)
System.out.println(a);
}
}
@Retention(RetentionPolicy.RUNTIME) // 실행 시에 사용 가능하도록 지정
@interface TestInfo{
int count() default 1;
String testedBy();
String[] testTools() default "JUnit";
TestType testType() default TestType.FIRST;
DateTime testDate();
}
@Retention(RetentionPolicy.RUNTIME)
@interface DateTime{
String yymmdd();
String hhmmss();
}
enum TestType { FIRST, FINAL }
anno.testedBy()=aaa
anno.testDAte().yymmdd()=160101
anno.testDate().hhmmss()=235959
testTools =JUnit
@java.lang.Deprecated(forRemoval=false, since="")
@ch12.TestInfo(count=1, testType=FIRST, testTools={"JUnit"}, testedBy="aaa", testDate=@ch12.DateTime(yymmdd="160101", hhmmss="235959"))