들어가며

우리는 Spring Framework를 사용하며 이미 많은 어노테이션을 사용해왔는데요
많은 자바 개발자분들이 @Controller, @Service, @Repository, @Component 등의 사용에 익숙하실거라 생각합니다.
오늘은 이러한 어노테이션 이외에 메타 어노테이션에 대해 알아보려고 합니다!!


@annotation

자바 어노테이션은 jdk5 부터 추가된 기능입니다. 특별한 기능은 아니지만 Source 코드에 추가적인 정보 (메타 데이터)를 제공하여 줍니다
따라서, @어노테이션을 사용하면 비즈니스 로직에 직접적인 영향을 미치지 않지만, 어노테이션 추가만으로 더 깔끔한 코딩이 가능해집니다


Built-in 어노테이션

빌트인 어노테이션이란 자바에서 기본적으로 제공해주는 어노테이션으로
컴파일러 경고 및 에러를 생성하여 코드를 형식에 맞게 제한하여 줍니다.
해당 어노테이션들은 java.lang 패키지에 속하며, 아래에 보이는 6개의 어노테이션이 존재합니다. 다들 많이 사용해 보셨을 거라 생각하고 설명은 생략하도록 하겠습니다

@Override, @Deprecated, @SuppressWarnings, @SafeVarargs, @FunctionalInterface, @Native


메타어노테이션

Target

@Target annotation은 해당 어노테이션의 적용범위를 Java Compilrer가 설정하는 것을 적용해주는 역할을 합니다. ElementType enum으로 범위를 설정해주어야 합니다. 설정 범위는 아래와 같습니다

ElementType.TYPE : 클래스, 인터페이스, enum
ElementType.FIELD : 필드
ElementType.METHOD : 메서드
ElementType.PARAMETER : 파라미터
ElementType.CONSTRUCTOR : 생성자
ElementType.LOCAL_VARIABLE : 지역 변수 
ElementType.ANNOTATION_TYPE : 어노테이션 
ElementType.PACKAGE : 패키지
ElementType.TYPE : 클래스, 인터페이스, enum
ElementType.TYPE_PARAMETER : 타입 파라미터
ElementType.TYPE_USE : 타입
ElementType.MODULE : 모듈
  • @Target()의 value로 ElementType을 지정하지 않으면 오류가 납니다

@Retension

@Retension 어노테이션은 해당 어노테이션이 적용되고 유지되는 범위를 설정해줍니다.
RetentionPolicy enum으로 범위를 설정해주어야 합니다. 설정 범위는 아래와 같습니다

RetentionPolicy.RUNTIME : 런타임
RetentionPolicy.CLASS : 컴파일
RetentionPolicy.SOURCE : 컴파일 전 소스레벨

Jvm 환경에서 실제로 사용하기 위해서는 RUNTIME을 적용해주어야 합니다.


@Documented

JavaDoc 생성시 Annotation에 대한 정보도 함께 생성하여 줍니다


참조

java.lang.annotation (Java Platform SE 8 )
Java Meta Annotation 메타 어노테이션 :: daily

블로그 이미지

yhmane

댓글을 달아 주세요

40 @Override 애너테이션을 일관되게 사용하라

@Override는 메서드 선언에만 달 수 있으며, 이 애너테이션이 달렸다는 것은 상위 타입의 메서드를 재정의했음을 뜻합니다.
애너테이션을 일관되게 사용하면 여러 가지 악명 높은 버그들을 예방해줍니다

public class Bigram {
    private final char first;
    private final char second;

    public Bigram(char first, char second) {
        this.first = first;
        this.second = second;
    }

    public boolean equals(Bigram bigram) {
        return bigram.first == this.first &&
                bigram.second == this.second;
    }

    public int hashCode() {
        return 31 * first + second;
    }

    public static void main(String[] args) {
        Set<Bigram> s = new HashSet<>();
        for (int i = 0; i < 10; i++) {
            for (char ch = 'a'; ch <= 'z'; ch++) {
                s.add(new Bigram(ch, ch));
            }
        }

        System.out.println(s.size());
    }
}
  • Main () 메서드를 살펴보면 바이그램 26개를 10번 반복해 집합에 추가한 다음 그 집합의 크기를 출력합니다
  • Set은 중복을 허용하지 않으니 26이 출력될거 같지만 실제로는 260이 출력됩니다
    • -> 여기서 equals를 재정의한게 아니라 다중정의를 하였습니다
  • HashSet은 내부적으로 equals 메서드를 기반으로 객체의 논리적 동치적(equals) 검사를 실시합니다
  • 하지만 equals메서드의 파라미터 타입이 Bigram입니다
    • 즉, equals 메서드를 재정의 한게 아니라 Overloading 하였습니다
    • equals를 재정의 하려면 파라미터 타입이 Object이어야 합니다
@Override
public boolean equals(Object bigram) {
    if(!(o instanceof Bigram)) {
        return false;
    }
    Bigram b = (Bigram) bigram;
    return b.first == this.first &&
        b.second == this.second;
}

정리

  • 상위 클래스의 메서드를 재정의 하는 모든 메서드에 @Override 애너테이션을 다는게 좋습니다
  • 인터페이스를 상속한 구체 클래스인데 아직 구현하지 않은 추상 메서드가 남아있다면 컴파일러가 알려줍니다
  • Java 8 부터 Default 메서드의 사용이 가능해 지면서, 인터페이스의 메서드를 재정의 할 때도 사용할 수 있습니다
  • 추상클래스나 인터페이스에서는 상위 클래스나 상위 인터페이스를 재정의하는 모든 메서드에 @Override를 다는것이 좋습니다
블로그 이미지

yhmane

댓글을 달아 주세요

38. 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라

타입 안전 열거 패턴은 확장이 가능하나, 열거 타입은 확장을 할 수 없습니다.
하지만, 인터페이스와 그 인터페이스를 구현하는 기본 열거 타입을 함께 사용해 같은 효과를 낼 수 있습니다

타입 안전 열거 패턴(typesafe enum pattern)

enum이 나오기전 jdk 1.5 밑에 버전에서는 아래와 같이 사용하였습니다

public final class Shape {

    private String polygon;

    private Shape() {
    }

    private Shape(String polygon) {
        this.polygon = polygon;
    }

    public static final Shape TRIANGLE = new Shape("triangle");
    public static final Shape RECTANGLE = new Shape("rectangle");
    public static final Shape PENTAGON = new Shape("pentagon");
}

열거타입 (enum)

Enum class cannot inherit from classes

public enum Shape {
    TRIANGLE, RECTANGLE, PENTAGON
}
  • 열거 타입은 타입 안전 열거 패턴보다 우수합니다
  • 단, 타입 안전 열거 패턴은 확장이 가능하나 열거 타입은 확장할 수 없습니다
  • 열거 타입을 확장하려면 열거 타입이 임의의 인터페이스를 구현할 수 있다는 사실을 이용하면 됩니다

인터페이스를 이용한 확장 가능 열거 타입

public interface Operation {
    double apply(double x, double y);
}
public enum BasicOperation implements Operation {

    PLUS("+") {
        @Override
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS("-") {
        @Override
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES("*") {
        @Override
        public double apply(double x, double y) {
            return x * y;
        }
    },
    DIVIDE("/") {
        @Override
        public double apply(double x, double y) {
            return x / y;
        }
    };

    private final String symbol;

    BasicOperation(String symbol) {
        this.symbol = symbol;
    }
}

열거 타입인 BasicOperation은 확장할 수 없습니다.
다만, 인터페이스인 Operation을 이용해 확장할 수 있고
이 인터페이스를 연산의 타입으로 이용할 수 있습니다

다른 열거 타입을 추가

public enum ExtendedOperation implements Operation {

    EXP("^") {
        @Override
        public double apply(double x, double y) {
            return Math.pow(x, y);
        }
    },
    REMAINDER("%") {
        @Override
        public double apply(double x, double y) {
            return x % y;
        }
    };

    private final String symbol;

    ExtendedOperation(String symbol) {
        this.symbol = symbol;
    }
}

Operation 인터페이스를 구현하면 다른 열거 타입에서도 확장할 수 있습니다


enum 타입 확인

  1. Enum타입을 넘겨 순회하기 (class literal 넘기기)
    public static void main(String[] args) {
     double x = 10;
     double y = 2;
     test(ExtendedOperation.class, x, y);
    }
    

public static <T extends Enum & Operation> void test(Class opEnumType, double x, double y) {

for (Operation op : opEnumType.getEnumConstants()) {
    System.out.printf(“%f %s %f = %f%n”, x, op, y, op.apply(x, y));
}

}


ExtendedOperation의 class 리터럴

<T extends Enum<T> & Operation> 
 타입이 Enum타입이면서, Operation을 구현하는 클래스

10 ^ 2 = 100
10 % 2 =0

2. 열거 타입의 리스트 넘기기
```java
public static void main(String[] args) {
    double x = 10;
    double y = 2;
    test(Arrays.asList(ExtendedOperation.values()), x, y);
}

public static void test(Collection<? extends Operation> opSet, double x, double y) {
    for (Operation op : opSet) {
        System.out.printf(“%f %s %f = %f%n”, x, op, y, op.apply(x, y));
    }
}

열거 타입의 리스트를 넘겨 <? extends Operation>인 한정적 와일드 카드 타입으로 지정

10 ^ 2 = 100
10 % 2 =0

정리

  • 열거 타입끼리 구현을 상속 할수는 없습니다
  • 확장할 수 있는 열거 타입이 필요한 경우 인터페이스 정의를 구현합니다
블로그 이미지

yhmane

댓글을 달아 주세요

어느덧 21년을 마무리하게 되었고,

작년을 되돌아보며 회고하는 시간을 가지려 합니다 

 

1. 이직 🤗

1월 퇴사를 하고 2월달 이직을 하게 되었습니다

스타트업의 대한 환상이 와르르 깨졌던 시기였습니다 😭

 

육체적, 정신적으로 많이 힘들었기에 이직을 고민하게 되었고,

때마침 지인분께서 제안을 주셨고, 이직을 하게 되었습니다

 

자세한 내용은 말씀드릴 수 없지만,

건강을 해치는 선택은 피하시길 바랍니다🙏


2. 개인의 성장 🦾

이직을 하게 되면서 많은 경험을 할 수 있었습니다 🥰

이전 경험했던 회사들과는 트래픽 양이 달랐기에

팀에서도 기술적으로 많은 시도를 하고 있었습니다

 

엘라스틱서치 클러스터를 운영하는게 좋은 경험이 되었고,

스프링배치, 코틀린, 테스트컨테이너, 카프카, 레디스, 엘라스틱서치 등

다방면으로 아키텍쳐를 설계하고 코드에 접할수 있었던 점이 좋았습니다

 

뿐만 아니라 팀원분들이 성장에 목말라 있었고, 

코드리뷰에 대해서도 적극적으로

임해주셨기에 감사하게 생각하고 있습니다!!! 🥰


3. 스터디 ✍️

스터디는 사람마다 다른 주제일 거 같습니다

혼자 공부하는걸 선호하시는분도 있고,

여러 사람들이 만나 스터디를 하는걸 좋아하는 사람도 있는데

저는 후자입니다!!

 

사실, 혼자 책상에 앉아 공부하는 걸 좋아하지 않습니다

공부할 때도 가능하면 카페에서 하는걸 선호하네요 ㅎㅎ

 

운좋게도 팀에서 스터디를 진행하고 있어서

오브젝트, 엘라스틱서치를 공부할 수 있었습니다!

 

최근엔 우아한스터디락에서 이펙티브자바를 다같이 공부하고 있는데요 

회사 일정으로 너무 바빠서 잘 참여하지 못하고 있는게 아쉬울 뿐입니다 ㅠㅠ

12월에 4개월동안 준비한 tf 배포건이 있어서 너무 바빴지만…

1월부터는 열심히 달려 볼려고 합니다!!


4. 21년, 읽은책 📚

  • 오브젝트
  • 모던 자바 인 액션
  • 클린코드
  • 엘라스틱서치 실무 가이드

 

개인적으로 오브젝트라는 책이 정말 훌륭한거 같네요!!


5. 22년도, 앞으로 읽을책📚

  • 이펙티브자바 3판 (읽고 있음)
  • HTTP 완벽 가이드
  • DDD start!
  • 실전! 스프링 5를 활용한 리액티브 프로그래밍
  • 아파치 카프카 애플리케이션 프로그래밍 with 자바

 

(스프링 개발자 책 추천해주세요!!)


6. 아쉬웠던 부분 & 목표 🤭

영어공부와 운동을 꾸준히 하겠다고 다짐 했었는데

지키지 못했습니다 ... 너무 게으른 제 자신을 반성합니다 ... ㅎㅎ

 

외국인과 데일리로 가볍게 통화할 수 있는 프로그램이 있다는데 한번 알아보려 합니다!!

운동은 예전부터 꾸준히 하려고 다짐했었는데 ...

올해는 꼭 몸은 만들어 보려고 합니다!! 다행히 헬스장은 등록했네요!! 

 

 

 

 

 

 

 

 

 

블로그 이미지

yhmane

댓글을 달아 주세요

28. 배열보다는 리스트를 사용하라

배열과 제네릭 타입에는 중요한 차이가 두가지 있습니다

1. 배열은 공변, 제네릭은 불공변입니다

// 배열, ArrayStoreException, RunTime 오류
Object[] objectArray = new Long[1];
objectArray[0] = “타입이 달라 넣을 수 없다”; 

// 리스트, 컴파일 되지 않는다
// List<Object> objectList = new ArrayList<Long>(); 

배열은 sub가 super의 하위타입이라면 sub[]는 배열 super[]의 하위타윕이 됩니다. 따라서, 위에 Long에 String을 입력하여도 컴파일 오류가 나지 않았습니다.

반면, 리스트 List은 List의 하위타입도 상위 타입도 아닙니다. 따라서, 위의 코드에서 컴파일 오류가 났습니다

2. 배열은 실체화가 되고, 리스트는 그렇지 않습니다

배열은 런타임에도 자신이 담기로 한 원소의 타입을 인지하고 확인합니다.
반면 제네릭은 타입정보가 런타임에는 소거됩니다. 이 두 차이로 인해 배열과 제네릭은 어울러질 수 없습니다

List<String>[] stringLists = new List<String>[1];

정리

타입 세이프하고, 런타임이 아닌 컴파일 단계에서 에러를 잡아줍니다. 다만, 성능으로는 배열이 유리하지만 웬만하면 리스트를 사용하도록 합니다

참조

effective java 3/e

블로그 이미지

yhmane

댓글을 달아 주세요

27. 비검사 경고를 제거하라

제네릭을 사용하기 시작하면 수많은 컴파일러 경고들을 마주치게 됩니다
이러한 경고들을 가능한 많이 제거하는 것이 습니다
경고들을 모두 제거한다면, 그 코드는 타입 안정성이 보장되기 때문입니다

대부분의 비검사 경고는 쉽게 제거할 수 있습니다

Set<Car> cars = new HashSet();
Venery.java:4: warning: [unchecked] unchecked conversion
                Set<Car> cars = new HashSet();
                                ^
  required: Set<Car>
  found:    HashSet

컴파일러가 알려준 대로 수정하면 경고가 사라집니다

자바7 이후 제공하는 다이아몬드 연산자<>로 해결이 가능합니다

Set<Car> cars = new HashSet<>();

다만, 제거하기 훨씬 어려운 경고들도 있습니다.

곧바로 해결되지 않는 경고가 나타나더라도 할 수 있는한 모든 비검사 경고를 제거하도록 합니다

경고를 제거할 수 없지만 타입이 안전하다고 확신할 수 있으면,  @SuppressWarnings(“unchecked”)를 달아 경고를 숨기도록 합니다

@SuppressWarnings

  • 지역변수 ~ 클래스 전체까지 모두 선언 할 수 있습니다
    • 가능한 가장 좁은 범위에 적용하도록 합니다
    • 또한, 클래스 전체에 적용하지 않도록 합니다
  • 한줄이 넘는 메서드나 생성자에 달린 @SuppressWarnings은 지역변수 선언 쪽으로 옮기도록 합니다
    • 이를 위해 지역변수를 새로 선언하는 수고를 해야할 수 있지만, 그만한 값어치가 있습니다
  • 또한 @ SuppressWarnings 사용시 안전한 이유를 항상 주석으로 남겨두도록 합니다 
  • public <T> T[] toArray(T[] a) { if (a.length < size) { // 생성한 배열과 매개변수로 받은 배열의 타입이 모두 T[]로 같으므로 올바른 형변환입니다 @SuppressWarnings("unchecked") T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass()); return result; } System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; }

참조

effective java 3/e

블로그 이미지

yhmane

댓글을 달아 주세요