1. 지네릭스(Generics)
☾ 지네릭스
: 컴파일 시 타입을 체크(compile-time type check) - jdk 1.5부터 도입
: 객체의 타입을 미리 명시함으로써 번거로운 형변환 감소
: 타입 안정성을 높이고 형변환의 번거로움을 줄여줌
class Box<T> { }
- Box<T> : 지네릭 클래스
- T : 타입 변수(매개변수)
- Box : 원시 타입
지네릭스 클래스의 선언
class Box<T> {
T item;
void setItem(T item) {
this.item = item;
}
T getTime() {
return item;
}
}
- 사용 방법
Box<String> b = new Box<String>(); // 타입 T 대신 String 타입 지정
// b.setItem(new Object); // 에러. String 이외의 타입은 지정 불가
b.setItem("ABC");
String item = b.getItem(); // 형변환 필요 없음
지네릭스의 제약사항
: static 멤버에는 타입 변수 T를 사용할 수 없음
class Box<T> {
static T item; // 에러
static int compare(T t1, T t2) {...} // 에러
}
: 지네릭 타입의 배열 T[] 생성 불가
class Box<T> {
T[] itemArr; // 가능. T타입의 배열을 위한 참조변수
T[] toArray() {
T[] tmpArr = new T[itemArr.length]; // 에러. 지네릭 배열 생성 불가
return tmpArr;
}
}
지네릭 클래스의 객체 생성과 사용
: 객체 생성 시 참조변수와 생성자에 대입된 타입이 일치해야 함
Box<Apple> appleBox = new Box<Apple>();
Box<Apple> appleBox = new Box<Grape>(); // 에러
Box<Fruit> appleBox = new Box<Apple>(); // 에러
: 두 지네릭 클래스가 상속관계이고 대입된 타입이 일치하는 것은 가능
Box<Apple> appleBox = new FruitBox<Apple>(); // 가능. 다형성
Box<Apple> appleBox = new Box(); // 가능. jdk 1.7부터 생략 가능
: 대입된 타입과 다른 타입의 객체는 추가할 수 없음
Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple());
appleBox.add(new Grape()); // 에러. Box<Apple>에는 Apple객체만 추가 가능
제한된 지네릭 클래스
: 지네릭 타입에 'extends' 사용하면 특정 타입의 자손들만 대입할 수 있음(메서드에서도 마찬가지)
class FruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정 가능
ArrayList<T> list = new ArrayList<T>();
void add(T item) {
list.add(item);
}
}
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
fruitBox.add(new Apple()); // Apple이 Fruit의 자손인 경우 가능
fruitBox.add(new Grape()); // Grape가 Fruit의 자손인 경우 가능
: 인터페이스의 경우에도 'implements' 대신 'extends' 사용
interface Eatable{}
class FruitBox<T extends Eatable> {...}
class FruitBox<T extends Fruit & Eatable> {...}
와일드 카드 '?'
: 지네릭 타입에 와일드 카드를 쓰면 여러 타입 대입 가능
: <? extends T & E> 와 같이 '&' 사용 불가
<? extends T> : 와일드 카드의 상한 제한. T와 그 자손들만 가능
<? super T> : 와일드 카드의 하한 제한. T와 그 조상들만 가능
<?> : 제한 없음. 모든 타입이 가능 == <? extends Object>
static Juice makeJuice(FruitBox<? extends Fruit> box) {
String tmp = "";
for (Fruit f : box.getList()) {
tmp += f + " ";
return new Juice(tmp);
}
}
지네릭 메서드
: 반환타입 앞에 지네릭 타입이 선언된 메서드
static <T> void sort(List<T> list, Comparator<? super T> c) {}
: 클래스의 타입 매개변수<T>와 메서드의 타입 매개변수<T>는 별개
class FruitBox<T> {
static <T> void sort(List<T> list, Comparator<? super T> c) {
}
}
: 지네릭 메서드를 호출할 때, 타입 변수에 타입을 대입해야 함
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> fruitBox = new FruitBox<Apple>();
System.out.println(Juicer.<Fruit>makeJuice(fruitBox));
System.out.println(Juicer.makeJuice(appleBox)); // 대입된 타입 생략 가능
지네릭 타입의 형변환
: 지네릭 타입과 원시 타입 간의 형변환은 가능 but 경고 발생
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<? extends Object> wBox = new Box<String>();
FruitBox<? extends Fruit> box = null;
FruitBox<Apple> appleBox = (FruitBox<Apple>)box;
지네릭 타입의 제거
: 컴파일러는 지네릭 타입을 제거하고 필요한 곳에 형변환을 넣음
- 지네릭 타입의 경계를 제거
- 지네릭 타입 제거 후에 타입이 불일치하면 형변환 추가
- 와일드 카드가 포함된 경우, 적절한 타입으로 형변환 추가
2. 열거형(enums)
☾ 열거형
: 관련된 상수들을 같이 묶어놓은 것
class Direction {
static final Direction EAST = new Direction("EAST");
static final Direction WEST = new Direction("WEST");
static final Direction SOUTH = new Direction("SOUTH");
static final Direction NORTH = new Direction("NORTH");
private String name;
private Direction(String name) {
this.name = name;
}
}
enum Direction {EAST, WEST, SOUTH, NORTH}
열거형의 정의와 사용
enum 열거형이름 {상수명1, 상수명2, ...}
enum Direction {EAST, WEST, SOUTH, NORTH}
class Unit {
int x, y;
Direction dir;
void init() {
dir = Direction.EAST;
}
}
- 열거형 상수의 비교
: ==와 compareTo() 사용 가능
if (dir == Direction.EAST) {
x++;
}
else if (dir.compareTo(Direction.WEST) > 0) {
x--;
}
else if (dir > Direction.WEST) { // 에러. 비교연산자 사용 불가
...
}
모든 열거형의 조상 - java.lang.Enum
: 모든 열거형은 Enum의 자손
enum Direction {EAST, WEST, SOUTH, NORTH}
public class EnumEx1 {
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 ? "+(d2 > 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 WEST:
System.out.println("The direction is WEST.");
break;
case SOUTH:
System.out.println("The direction is SOUTH.");
break;
case NORTH:
System.out.println("The direction is NORTH.");
break;
default:
System.out.println("Invalid direction");
}
Direction[] dArr = Direction.values();
for (Direction d : dArr) {
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) ? -1
The direction is EAST.
EAST=0
WEST=1
SOUTH=2
NORTH=3
열거형에 멤버 추가
: 불연속적인 열거형 상수의 경우, 원하는 값을 괄호() 안에 적음
enum Direction {EAST(1), WEST(-1), SOUTH(5), NORTH(10)}
: 괄호를 사용하려면 인스턴스 변수와 생성자를 새로 추가해야 함
: 열거형의 생성자는 묵시적으로 private이므로 외부에서 객체 생성 불가
enum Direction {
EAST(1), WEST(-1), SOUTH(5), NORTH(10);
private final int value;
Direction(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
3. 어노테이션(Annotation)
☾ 어노테이션
: 주석처럼 프로그래밍 언어에 영향을 미치지 않으며 유용한 정보 제공
@Test // 이 메서드가 테스트 대상임을 테스트 프로그램에게 알림
public void method() {
...
}
표준 어노테이션
@Override
: 오버라이딩을 올바르게 했는지 컴파일러가 체크하게 함
: 오버라이딩할 때는 메서드 선언부 앞에 @Override 붙이는 것이 좋음
class Parent {
void parentMethod() {}
}
class Child extends Parent {
@Override
void parentMethod() {}
}
@Deprecated
: 앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙임
@Deprecated
public int getDate() {
return normalize().getDayOfMonth();
}
@FunctionallInterface
: 함수형 인터페이스에 붙이면, 컴파일러가 올바르게 작성했는지 체크
※ 함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약 있음
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@SuppressWarnings
: 컴파일러의 경고메시지가 나타나지 않게 억제
: 괄호()안에 억제하고자 하는 경고의 종류를 문자열로 지정
: 둘 이상의 경고를 동시에 억제하려면 콤마(,)로 구분
: '-Xlint' 옵션으로 컴파일하면 경고메시지 확인 가능
@SuppressWarnings("unchecked")
ArrayList list = new ArrayList();
list.add(obj);
@SafeVarargs
: 가변인자의 타입이 non-reifiable인 경우 발생하는 unchecked 경고 억제
: 생성자 또는 static, final이 붙은 메서드에만 붙일 수 있음
: @SafeVarargs에 의한 경고의 억제를 위해 @SuppressWarnings 사용
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
메타
: 메타 어노테이션은 어노테이션을 위한 어노테이션
: 어노테이션을 정의할 때, 적용대상이나 유지기간의 지정에 사용
@Target
: 어노테이션을 적용할 수 있는 대상의 지정에 사용
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@Retention
: 어노테이션이 유지되는 기간을 지정하는데 사용
: 컴파일러에 의해 사용되는 어노테이션의 유지 정책은 SOURCE
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {}
: 실행 시에 사용 가능한 어노테이션의 정책은 RUNTIME
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
@Documented
: javadoc으로 작성한 문서에 포함시키는데 사용
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}
@Inherited
: 어노테이션을 자손 클래스에 상속할 때 사용
@Inherited
@interface SupperAnno {}
@SupperAnno
class Parent {}
class Child extends Parent {} // Child에 어노테이션이 붙은 것으로 인식
@Repeatable
: 반복해서 붙일 수 있는 어노테이션을 정의할 때 사용
@interface ToDos {
ToDo[] value();
}
@Repeatable(ToDos.class) // ToDo어노테이션을 여러 번 반복해서 쓸 수 있게 함
@interface ToDo {
String value();
}
@ToDo("delete test codes")
@ToDo("override inherited methods")
@Native
: native메서드에 의해 참조되는 상수에 붙임
@Native public static final long MIN_VALUE = 0x8000000000L;
어노테이션 타입 정의
: 어노테이션의 메서드는 추상메서드이며, 어노테이션을 적용할 때 모두 지정해야 함
@interface 어노테이션이름 {
타입 요소이름();
}
어노테이션 요소의 기본값
: 값을 지정하지 않으면 사용될 수 있는 기본값 지정 가능(null 제외)
@interface TestInfo {
int count() default 1;
}
@TestInfo // TestInfo(count=1)과 동일
public class NewClass {}
: 요소의 이름이 value인 경우 생략 가능
@interface TestInfo {
String value();
}
@TestInfo("passed") // @TestInfo(value="passed")와 동일
public class NewClass{}
: 요소의 타입이 배열인 경우 중괄호{} 사용
@interface TestInfo {
String[] info1() default {"aaa", "bbb"};
String[] info2() default "ccc";
}
@TestInfo
@TestInfo(info2= {})
class NewClass {}
모든 어노테이션의 조상 - java.lang.annotation.Annotation
: Annotation은 모든 어노테이션의 조상이지만 상속 불가
: Annotation은 인터페이스로 정의되어 있음
@interface TestInfo extends Annotation { // 에러
int count();
String testedBy();
}
마커 어노테이션 - Marker Annotation
: 요소가 하나도 정의되지 않은 어노테이션
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {} // 마커 어노테이션
어노테이션 요소의 규칙
- 요소의 타입은 기본형, String, enum, 어노테이션, Class만 허용됨
- 괄호()안에 매개변수 선언 불가
- 예외 선언 불가
- 요소를 타입 매개변수로 정의 불가
'Back-end > JAVA' 카테고리의 다른 글
[JAVA] JDK & IntelliJ 버전 변경(Windows) (0) | 2024.01.05 |
---|---|
[JAVA] 컬렉션 프레임워크(Collections Framework) (1) | 2023.10.21 |
[JAVA] 날짜와 시간 & 형식화 (1) | 2023.10.14 |
[JAVA] java.lang 패키지와 유용한 클래스 - Object, String, Math, wrapper 클래스 (0) | 2023.10.07 |
[JAVA] 예외처리 - try-catch문, 예외 선언, 예외 되던지기 (0) | 2023.09.23 |