[Java] Code Convention

livenow·2020년 11월 25일
0
post-thumbnail

[Java] Code Convention

개발자라면 협업 시, 사용하는 언어의 코드 컨벤션을 지켜야 합니다.

이전까지는 느낌상 이렇게 하면 되겠다는 생각으로 개발을 했었는데,

굉장히 위험한 생각인 것 같아 정리하게 되었습니다.

이 포스트는 Google Java Style Guide 의 내용을 토대로 썼습니다.

1. 소개

Java 소스 파일은 아래의 규칙을 준수하는 경우에만 Google 스타일로 설명이 됩니다. Google에서는 어떤 코딩 스타일을 따르고 있는지 알아봅니다.

1.1 용어 참고

달리 명시되지 않는 한,

1. 클래스라는 용어는 "일반"클래스, 열거 형 클래스, 인터페이스 또는 주석 유형(@interface)을 의미하기 위해 포괄적으로 사용됩니다.

2. 클래스의 멤버라는 용어는 중첩 된 클래스, 필드, 메서드 또는 생성자를 의미하기 위해 포괄적으로 사용됩니다.

3. 주석이라는 용어는 항상 구현 주석을 의미합니다. "Documentation comments"라는 이름을 사용하지 않고 "Javadoc"이라는 일반적인 용어를 사용합니다.


2. 소스파일 기본

2.1 파일이름

- 클래스 이름과 동일하게 대소문자를 구별해서 작성. 확장자는 java.
-- ex) Car class가 구현된 소스파일 이름: Car.java

2.2 소스파일 인코딩

- 인코딩은 UTF-8로 통일.

2.3 공백문자

- 스페이스키만 허용.

2.4 특수 문자

- 특수문자를 사용할 때는 (\b, \t, \n, \f, \r, ", ' and \)을 사용.
- special escape sequence를 참고

2.5 유니코드

- 코드의 가독성을 높일 수 있다면 유니코드를 사용해도 됨.


3. 소스파일 구조 (소스 파일내 코드 선언 순서)

소스코드는 위 형태로 선언되야함. 각 섹션 사이에는 공백 라인이 하나 들어감.

3.1 라이센스

- 라이센스 또는 저작권 정보가 파일에 속하면 기입.

3.2 Package 문

- 아무리 길어도 줄바꿈을 하지 않음.

3.3 Import 문

- 와일드 카드(e.g. *)를 쓰지 않음, Package 문과 동일하게 줄바꿈 하지 않음.
- 그룹핑을 해서 순서에 맞춰 작성. 다른 그룹간에는 공백라인을 한 줄 추가.
- 그룹 이름 및 순서:
-- static import
-- non - static imports

3.4 Class 선언

- 최상위 클래스만 선언
- 클래스 멤버(1.1 참고)의 순서는 절대적인 것이 없음. 하지만 이들의 순서가 논리적이여야 함.
- 새로운 메소드가 추가되었다고 해서 클래스의 가장 마지막에 구현하는 것은 논리적이지 않음.
- 동일한 메소드명 (생성자들, 오버라이딩된 메소드들) 은 한 곳에 모음.


4. 포맷팅

4.1 중괄호

- K & R 스타일을 따름
- 본문이 비어 있거나 단일 문만 포함하는 경우에도 if, else, for, do 및 while 문과 함께 사용

  • 틀린 예

    if (money > price)
        return true;
    else
        return false;
  • 옳은 예

    if (money > price) {
        return true;
    }
    else {
        return false;
    }

- 여는 중괄호 뒤에는 코드가 없어야 함
- 닫는 중괄호 뒤에는 코드가 없어야 함
- 문장에 닫는 중괄호만 있는 케이스는 함수가 끝나거나 제어문이 끝날 때.
- 코드가 없는 메소드의 경우는 그냥 닫아도 괜찮음.

   // This is acceptable
     void doNothing() {}

     // This is equally acceptable
     void doNothingElse() {
     }

4.2 들여쓰기

- 스페이스키 2개로 정의

4.3 한 문장에 하나의 statement

- 각 문장 뒤에는 줄 바꿈을 함

4.4 줄길이 제한: 100

- 한 라인의 문자는 100개만 씀. 그 보다 길 경우 다음 라인으로 내림.

4.5 Line-wrapping( 줄 바꿈 )

- 코드의 길이가 페이지 넓이를 넘어갈 때, 하나의 문장을 두 문장 이상으로 나눠서 표현하는 것.
- 절대적인 법칙은 없고 상황에 따라서 적절하게 사용하면 됨
- 줄 바꿈의 일반적인 이유는 열 제한이 넘치지 않도록하는 것이지만 실제로 열 제한에 맞는 코드도 작성자의 재량에 따라 줄 바꿈 될 수 있음.
- 줄바꿈을 하기전, 메소드 추출을 이용해도 됨

4.5.1 언제 줄바꿈을 하는지

  • a) 문장을 대임 연산자가 아닌 곳에서 잘라야 할 경우 심볼 압에서 내림.

    List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        numbers.stream()
            .filter(i -> i % 2 == 0)
            .distinct()
            .forEach(System.out::println);
    
    @Override
        public String toString() {
            return "Order{" +
                    "memberId=" + memberId +
                    ", itemName='" + itemName + '\'' +
                    ", itemPrice=" + itemPrice +
                    ", discountPrice=" + discountPrice +
                    '}';
  • b) 대입 연산자에서 잘라야 할 경우 대입 연산자 뒤에서 문장을 내림

    AnnotationConfigApplicationContext ac = 
                new AnnotationConfigApplicationContext(SameBeanConfig.class);
  • c) 함수호출의 경우 '('는 첫 문장에 두고 나머지를 다음 문장으로 내림

    Assertions.assertThat(
                    order.getDiscountPrice()).isEqualTo(1000);
  • d) 콤마 '.'의 경우, 앞의 식별자와 동일한 단어로 취급

    	public void plus(int a, int b, int v, int d,
    							 int e, int f, int g) {
    

4.5.2 Line-wrapping 된 문장, 기본적으로 2번 이상의 들여쓰기

여러 문장이 연속해서 내려올 경우 첫 번째 내려온 문장과 동일한 들여쓰기를 유지

4.6 공백

4.6.1 공백 라인

- 클래스 멤버들을 구별하는 데 사용( 메소드, 생성자, 멤버 변수 )
- 멤버변수의 경우, 사이에 코드가 없다면 굳이 공백라인을 넣지 않아도 됨.
- 메소드 내부에서 논리적으로 그룹핑 되는 부분

4.6.1 공백 문자

- if, for, catch 와 ‘(‘ 사이에 공백문자
- else, catch와 ‘}’ 사이에 공백문자
- ‘,’, ‘:’, ‘;’ 다음 이나 타입 캐스트시의 ‘)’ 다음에 공백문자
- 연산자 앞 뒤로는 공백문자 삽입

  • 연산자와 비슷한 심볼에서도 앞 뒤로 공백문자 삽입
    Boolean test = true;
    if (test) {
        //...
    } else {
        //...
    }
    int testNum = 2 + 1 * (4 / 2) - 25;

    for (int i = 0; i < 3; i++) {
        //        
    }

4.8 나머지

4.8.1 Enum 클래스

- 열거 형 상수 뒤에 오는 각 쉼표 뒤에 줄 바꿈은 선택 사항.
- 추가 빈 줄 (일반적으로 하나만)도 허용됨

    private enum Answer {
      YES {
        @Override public String toString() {
          return "yes";
        }
      },

      NO,
      MAYBE
    }

- 메서드가없고 상수가 없는 열거 형 클래스는 선택적으로 배열 이니셜 라이저 인 것처럼 형식화 될 수 있음

 private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }

4.8.2 변수 선언

4.8.2.1 선언 당 하나의 변수

- 모든 변수 선언 (필드 또는 로컬)은 하나의 변수만 선언 e.g. int a, b; //사용되지 않음

int a;
int b;

4.8.2.1 필요할 때 선언

무조건적으로 시작부분에 시작하는 것은 아님
- 지역 변수는 범위를 최소화하기 위해 처음 사용되는 지점에 가깝게 선언됨
- 지역 변수 선언에는 일반적으로 이니셜 라이저가 있거나 선언 직후에 초기화됨.

4.8.3 배열

4.8.3.1 배열 초기화는 블록 형태도 가능

- 밑의 형식이 다가능함. (전체 목록이 아님)

new int[] {           new int[] {
  0, 1, 2, 3            0,
}                       1,
                        2,
new int[] {             3,
  0, 1,               }
  2, 3
}                     new int[]
                          {0, 1, 2, 3}

4.8.3.2 C 스타일 배열 선언 안됨

- 대괄호는 변수가 아닌 유형의 일부를 형성.
- String args []가 아니라 String [] args로 선언.

4.8.4 Switch 문

- switch문 자체가 레거시일 수 있으니, 안 알아 봄.

4.8.5 애노테이션

- 클래스, 메서드 또는 생성자에 적용되는 애노테이션은 문서 블록 바로 뒤에 나타나며, 각 애노테이션은 자체 줄에 나열됨. (즉, 한 줄에 하나의 주석)

  • 이러한 줄 바꿈은 들여 쓰기 수준이 증가하지 않음
@Override
@Nullable
public String getNameIfPresent() { ... }
  • 이것도 가능
@Override public int hashCode() { ... }
  • 이것도 가능
@Partial @Mock DataLoader loader;

4.8.6 주석 스타일

/*
 * This is          // And so           /* Or you can
 * okay.            // is this.          * even do this. */
 */

4.8.7 Modifier 순서

public protected private abstract default static final transient volatile synchronized native strictfp

4.8.8 숫자 리터럴

3000000000l 이 아닌 3000000000L 사용 

5. 네이밍

- 모든 식별자들은 ASCII와 숫자 값만 사용해야 함.
- 식별자에 prefix나 suffixes는 사용하지 않음. 즉, 아래와 같은 형태의 식별자는 사용되지 않음.

name_
mName
s_name
kName

5.2 식별자 유형별 규칙

5.2.1 Package 명

- 모두 소문자로 기술. 단어가 달라지더라도 무조건 소문자를 사용.

  com.example.deepspace (O)
  com.example.deepSpace (X)
  com.example.deep_space (X)

5.2.2 Class 명

- UpperCarmelCase를 사용. 이는 대문자로 시작하고 단어가 바뀔 때마다 다시 대문자로 표시. e.g. HelloWorld.java
- 테스트 클래스의 경우 마지막에 Test로 끝나도록 함. (HashIntegrationTest)

5.2.3 메소드 명

- lowerCarmelCase를 사용. 이는 소문자로 시작하고 단어가 바뀔 때마다 다시 대문자로 표시. e.g. printHelloWorld()
- 테스트 클래스의 경우 마지막에 Test로 끝나도록 함. (HashIntegrationTest)

5.2.4 상수 명

- 상수
-- 내용이 완전히 불변하고 메서드에 감지 가능하고 사이드 이펙트가 없는 정적 최종 필드.
-- 여기에는 프리미티브, 문자열, 불변 유형 및 불변 유형의 불변 컬렉션이 포함.
- CONTANT_CASE 방식을 사용. 이는 모두 대문자를 사용하며 단어 사이에 밑줄을 표시. 당연히 명사나 명사구여야 한다

// Constants
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final ImmutableMap<String, Integer> AGES = ImmutableMap.of("Ed", 35, "Ann", 32);
static final Joiner COMMA_JOINER = Joiner.on(','); // because Joiner is immutable
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }

// Not constants
static String nonFinal = "non-final";
final String nonStatic = "non-static";
static final Set<String> mutableCollection = new HashSet<String>();
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable);
static final ImmutableMap<String, SomeMutableType> mutableValues =
    ImmutableMap.of("Ed", mutableInstance, "Ann", mutableInstance2);
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};

5.2.5 멤버변수명 / 인자명 / 로컬변수명

- lowerCarmerCase를 사용.
- 메소드명과 다른 점은 동사가 아닌 명사라는 점. 한문자는 피함.


6. 프로그래밍 관례

6.1 @Override는 무조건 사용

- 오버라이딩이 된 모든 경우에**@Override는 필수로 기입**
- 부모 클래스의 메소드를 재정의 하거나 인터페이스를 구현했을 때도 마찬가지.
- 다만 부모 쪽에서 @Deprecated를 선언했을 경우에는 자식 쪽에서 @Override를 생략가능.

6.2 모든 예외 처리

- 모든 예외는 무시하지 말고 처리.
- 만약 예외를 처리하지 않을 거면 그 이유에 대해서 명확하게 주석을 담
- 테스트 코드에서는 필요시 무시 가능.

6.3 Static 멤버 접근

- 클래스 명으로 접근.

Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad

7. javadoc

/**
 * Multiple lines of Javadoc text are written here,
 * wrapped normally...
 * @author Livenow
 */
public class JavaDoc{ 
    /**
    * 곱셈을 합니다.
    * @param a
    * @param b
    * @return int
    */
    public int method(int a, int b) {
     return a * b;
    }
}

- /** 다음은 공백.
- 문단과 문단 사이에는 공백라인이 들어가고 @ 시작하기 전에도 공백라인이 들어감.
- @param, @return, @throws, @deprecated 순으로 사용.
- 설명은 무조건 기술해야 하며 한 문장을 넘어가면 4개 이상의 스페이스로 들여쓰기 함.


결론

대충 생각했던 Convention에 이렇게 많은 규약이 있는지 몰랐습니다.
협업을 진행하면서 꼭 생각을 하며 코드를 작성해야겠습니다.

Refer

profile
경험의 연장선

0개의 댓글