본문 바로가기

Programming/기초지식

Google Java Style Guide

반응형

해당 포스트는 아래 문서를 번역한 것입니다.

번역이 아닌 개인 공부를 목적으로 하기 때문에 본인이 이해한 대로 작성했고, 본문 내용 이외에 부가 정보 또한 추가했습니다. 

 

혹시 잘못된 정보가 포함되어 있는 경우 즉시 댓글로 관련 내용을 언급해 주시면 대단히 감사하겠습니다.

 

https://google.github.io/styleguide/javaguide.html#s1.1-terminology

 

Google Java Style Guide

1 Introduction This document serves as the complete definition of Google's coding standards for source code in the Java™ Programming Language. A Java source file is described as being in Google Style if and only if it adheres to the rules herein. Like ot

google.github.io

 

2 소스 파일 기본

2.1 파일 이름

소스 파일 이름은 최상위 클래스의 이름을 대소문자로 구분한 형태를 가진다.

파일 이름에. java 확장자를 추가해야 한다.

 

2.2 파일 인코딩

소스 파일은 UTF-8 형식으로 인코딩 된다.

 

2.3 특수 문자

 

2.3.1 공백 문자

줄 바꿈 문자(line terminator)를 제외하고 아스키코드의 공백 문자(0x20)가 소스 파일에 사용될 수 있는 유일한 공백 문자이다. 

 

문자열(string) 또는 문자(char) 내부에 있는 공백 문자들은 모두 이스케이프 문자 형식으로 변환된다.

탭 문자는 들여 쓰기에 사용되지 않는다.(키보드 Tab 키는 공백 8개에 해당한다. Tab 키는 'Tab 문자'라는 보이지 않는 문자를 만든다.)

 

2.3.2 특수 이스케이프 시퀀스

특수 이스케이프 시퀀스(\b, \t, \n, \f, \r, \", \' and \\)를 가진 문자의 경우 특수 이스케이프 시퀀스에 상응하는 8진수(예: \012) 또는 유니코드(예: \u000a) 대신 특수 이스케이프 시퀀스를 사용한다.

 

2.3.3 Non-ASCII 문자

Non-ASCII 문자의 경우 실제 유니코드 문자 또는 그와 동등한 유니코드 이스케이프가 사용된다.

유니코드가 외부 문자열 리터럴을 이스케이프하고 주석을 권장하지 않지만 코드의 가독성 때문에 해당 방법이 사용된다.

 

Tip: 유니코드 이스케이프 또는 유니코드 문자가 사용되는 경우, 설명을 포함한 주석을 사용하는 것은 도움이 된다.

 

예시

Example Discussion
String unitAbbrev = "μs"; 최적: 주석 없이도 완벽하다.
String unitAbbrev = "\u03bcs"; // "μs" 허용: 하지만 굳이 그렇게 할 필요는 없다. 
String unitAbbrev = "\u03bcs"; // Greek letter mu, "s" 허용: 하지만 어색하고 실수로 이어질 가능성이 높다.
String unitAbbrev = "\u03bcs"; 별로: 읽는 사람 입장에서 무슨 말을 하고 있는지 모른다.
return '\ufeff' + content; // byte order mark 좋음: 출력할 수 없는 문자들에 대해 이스케이스프를 사용하고, 필요 시 주석을 사용한다.

 

Tip: 절대로 몇몇 프로그램들이 Non-ASCII 문자를 제대로 인식하지 못할 것이라는 불안감 때문에 당신의 코드의 가독성을 해치는 짓은 하지 말 것. 만약 그렇다면 그 프로그램은 망가지고 보수되어야 한다.

 

3 소스 파일 구조

소스 파일은 다음과 같은 순서로 구성된다.

  1. 라이센스 또는 저작권 정보(있는 경우)
  2. 패키지(Package) 구문
  3. 주요 구문
  4. 정확히 하나의 최상위 클래스

정확히 하나의 공백 라인이 현재 존재하는 각 섹션을 분리한다.

 

3.1 라이센스 또는 저작권 정보(있는 경우)

만약 파일에 라이센스 또는 저작권 정보가 있는 경우 소스 파일 구조 1 순위로 구성된다.

 

3.2 패키지(Package) 구문

패키지 구문은 줄 바꿈 되어있지 않다.

패키지 구문에 열(column) 제한이 적용되어 있지 않다.

 

3.3 임포트 구문(Import statements)

 

3.3.1 와일드카드 임포트 없음

static 또는 다른 종류이든 와일드카드 임포트는 사용하지 않는다.

 

와일드카드 문자(wildcard character)는 컴퓨터에서 특정 명령어로 명령을 내릴 때, 여러 파일을 한꺼번에 지정할 목적으로 사용하는 기호를 가리킨다. 아래와 같이 '*'를 사용해서 해당 패키지 하위 클래스를 모두 임포트 하는 경우를 말한다.

import java.awt.*;

 

3.3.2 줄 바꿈 없음

임포트 구문은 줄 바꿈을 하지 않습니다. 

 

3.3.3. 순서 및 간격

임포트(import)는 다음 순서를 따른다.

  1. 하나의 블록에 모든 static을 임포트 한다.
  2. 하나의 블록에 모든 non-static을 임포트 한다.

간단히 말해서, 아래와 같이 임포트 할 때 한 줄에 하나만 하라는 말이다.

import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;

 

만약 static, non-static 임포트 둘 다 있다면, 두 개의 단일 블록으로 분리된다. 그 이외의 개행은 없다.

각 블록의 임포트 된 구문은 ASCII 순서로 정렬된다.(참고: '.'는 ';' 이전에 위치하기 때문에 A임포트 구문 내에서는 ASCII 순서가 아니다.)

 

3.3.4 클래스에 static 임포트 하지 않는다.

Static 임포트는 정적 중첩 클래스에 사용되지 않는다. 

중첩 클래스는 일반 임포트로 임포트 된다.

 

3.4 클래스 선언

 

3.4.1 정확히 하나의 최상위 클래스를 선언한다.

소스 파일마다 각자의 최상위 클래스가 존재한다.

 

3.4.2 클래스 내용 순서 정하기

멤버들과 클래스 초기화들의 순서를 정하는 것은 코드 가독성에 큰 영향을 미친다. 각 클래스의 내용에 따라 순서를 정하는 방법은 천차만별이므로 명확한 방법은 없다.

 

중요한 점은, 클래스는 논리적으로 설명할 수 있는 순서를 가지고 있어야 한다. 예를 들어 새로운 메서드를 '추가된 날짜순' 기준으로 습관적으로 클래스 끝부분에 추가하는 것은 논리적이지 못하다.

 

3.4.2.1 오버로드 : 분리하지 말 것

클래스가 여러 개의 생성자를 가지거나 동일한 이름의 여러 개의 메서드를 가지는 경우 이들을 순차적으로 작성해야 한다. 중간에 다른 코드가 끼어들면 안 된다.(private 멤버일지라도)

 

4 포매팅(Formatting)

용어 관련 참고 : block-like construct(괄호로 구성된 블록 형태의 구조)는 클래스 바디, 메서드 또는 생성자를 나타낸다. 섹션 4.8.3.1의 배열 초기화 또는 어떤 종류의 배열 초기화도 block-like construct 형태로 간주된다.

 

4.1 중괄호(Braces)

 

4.1.1 중괄호는 선택 사항일 경우(optional) 사용된다.

중괄호는 if, else, for, do, while 구문과 함께 사용한다. 바디가 없거나(empty) 한 줄의 구문만 있어도 사용한다.

 

4.1.2 비어있지 않은 블록(Nonempty blocks) : K & R 스타일

비어있지 않은 블록과 block-like construct 형태에서의 중괄호는 Kernighan and Ritchie style을 따른다.(이집트 괄호_Egyptian bracket라고 불린다) 

  • 여는 중괄호 앞에서 줄 바꿈 하지 않는다.
  • 여는 중괄호 뒤에서 줄 바꿈.
  • 닫는 중괄호 앞에서 줄 바꿈.
  • 닫는 중괄호 뒤에서 줄 바꿈. 오직 메서드, 생성자, 익명이 아닌 클래스(named class) 바디가 끝나는 경우에만. 만약 중괄호 뒤에 else 또는 콤마가 붙는 경우 줄 바꿈 없다.

예시

return () -> {
  while (condition()) {
    method();
  }
};

return new MyClass() {
  @Override public void method() {
    if (condition()) {
      try {
        something();
      } catch (ProblemException e) {
        recover();
      }
    } else if (otherCondition()) {
      somethingElse();
    } else {
      lastThing();
    }
  }
};

 

Enum 클래스의 경우 몇 가지 예외가 있는데 4.8.1에서 확인할 수 있다.

 

4.1.3 빈 블록 : 아마 간결하게

빈 블록 또는 block-like construct는 K & R 스타일을 따를 수 있다. 대안으로 괄호 사이에 아무런 문자나 줄바꿈 없이 열자마자 닫을 수 있다.( void test() {} <-- 이런 모양 )

이것은 멀티 블록 구문(if else 또는 try-catch-finally 형태)인 경우 해당사항이 아니다.

 

예시

  // 허용
  void doNothing() {}

  // 허용
  void doNothingElse() {
  }
  
  // 허용하지 않음 : 멀티 블록 구문에서는 명확한 빈 블록 형태를 사용할 수 없다.
  try {
    doSomething();
  } catch (Exception e) {}

 

4.2 블록 들여 쓰기: 2칸 띄우기

새로운 블록 또는 block-like construct가 열릴 때마다 두 칸 들여 쓰기가 된다. 블록이 끝나면 이전의 들여 쓰기 레벨로 돌아온다. 들여쓰기 레벨은 블록의 코드와 주석에 적용된다. 

(4.1.2의 예시를 확인해보자)

 

4.3 한 줄에 하나의 구문

각 구문이 끝나면 줄 바꿈을 한다.

 

4.4 열(column) 제한 : 100

자바 코드는 100개의 문자로 제한한다. '문자'는 어떤 유니코드 코드 포인트를 뜻한다. 아래 예외 사항을 제외하고, 제한을 넘는다면 줄 바꿈을 해야 한다. 

 

각 유니코드 코드 포인트는 화면에 표시되는 문자 크기에 상관없이 하나의 문자로 카운트한다. 만약 전각 문자를 사용한다면, 필요에 따라 문자 제한 길이를 초과하기 전에 줄바꿈을 할 수도 있을 것이다.

 

예외 사항

  1. 열 제한이 필수적이지 않은 라인이 있다.(ex: Javadoc의 긴 URL, 긴 길이의 JSNI 메서드 레퍼런스)
  2. package와 import 구문(3.2와 3.3 내용을 확인해보자)
  3. 쉘에 복사 붙여 넣기 하는 주석 안의 커맨드 라인

 

4.5 줄 바꿈(Line-wrapping)

용어 관련 참고 : 한 줄의 코드를 여러 줄로 분리하여 표현하는 것을 줄 바꿈이라고 한다.

 

발생하는 모든 상황에 대한 명확한 줄 바꿈 가이드 같은 것은 없다. 다만 몇 가지 자주 사용되는 방법이 있으니 해당 방법을 따르도록 한다.

 

참고: 일반적으로 열 제한을 피하기 위해 줄 바꿈을 한다. 하지만 개발자의 의도에 따라 얼마든지 다른 목적으로 줄 바꿈이 수행될 수 있다.

 

Tip: 메서드나 지역 변수를 가져오는 것으로 줄 바꿈을 대신할 수 있다.

 

4.5.1 줄 바꿈 위치

높은 구문 레벨에서 줄 바꿈을 수행하는 것이 일반적이다. 그 외의 경우는 아래와 같다.

  1. 비 할당 연산자(non-assignment operator)에서 줄 바꿈이 일어난다. 기호 앞에서 줄이 바뀐다.(C++이나 JavaScript에는 적용되지 않는 규칙이다)

위의 규칙은 다음의 '연산자 같은_operator-like' 기호에도 적용된다.

- 점 구별자(. )

- 메서드 레퍼런스의 두 개의 콜론( :: )

- 타입 바운딩의 앰퍼샌드( & )

- catch 블록 파이프( catch (FooException | BarException e) ).

 

2. 할당 연산자(assignment operator)에서 줄 바꿈이 일어났을 때 일반적으로 기호 뒤에 줄 바꿈이 오지만, 그 반대의 경우도 상관없다. 해당 상황은 향상된 for문 구문에서 assignment-operator-like 콜론에서도 적용된다.

 

3. 함수나 생성자의 이름에 여는 괄호( ( )가 있을 때 줄 바꿈.

 

4. 콤마( , ) 앞에 오는 토큰에 연결되어 있을 때 줄 바꿈.

 

5. 람다 화살표 표현식 근처에서는 절대 줄 바꿈 하지 않는다. 람다식이 괄호를 포함하지 않는(unbraced) 하나의 표현식으로 구성되어 있다면 화살표 이후에 바로 줄 바꿈 해도 괜찮다.

MyLambda<String, Long, Object> lambda =
    (String label, Long value, Object obj) -> {
        ...
    };

Predicate<String> predicate = str ->
    longExpressionInvolving(str);

 

4.5.2 연속 들여 쓰기는 최소한 +4 스페이스

줄 바꿈을 할 때 원래 줄에서 최소한 +4 스페이스만큼의 들여 쓰기를 한다.

여러 개의 연속적인 줄이 있는 경우 +4 이상의 들여 쓰기로 유연하게 변동될 수 있다.

일반적으로 두 개의 연속적인 줄이 구문적으로 병렬적인 요소를 가진다면 동일 레벨의 들여 쓰기를 사용한다.

 

 

4.6 공백(Whitespace)

 

4.6.1 수직 공백(Vertical Whitespace)

한 줄 공백은 다음 상황에서 사용된다.

 

  1. 연속된 멤버 또는 클래스 초기자(initializer) 사이 : 필드, 생성자, 메서드, 중첩 클래스, 정적 초기자, 인스턴스 초기자.

예외: 두 개의 연속된 필드 사이의 공백 라인은 선택사항이다. 이런 공백 라인은 필드의 논리적 그룹을 만들 때 사용된다.

예외: enum 상수들 사이의 공백 라인은 4.8.1에서 다루겠다.

 

2. 이 문서의 다른 섹션에서 요청되는 경우(섹션 3의 소스 파일 구조나 섹션 3.3의 임포트 구문)

 

공백 줄은 가독성을 높이는 용도로도 사용된다. 구문을 논리적 하위 섹션으로 구성하기 위한 경우가 그 예다. 첫 번째 멤버 또는 초기자 전에 공백 줄이 오거나 마지막 멤버 또는 초기자 이후에 공백 줄이 오는 경우가 있는데, 이는 권장되지도 권장되지도 않는다.(무슨 말?)

 

연속된 여러 공백 줄의 사용은 허용된다. 하지만 권장되지 않는다.

 

4.6.2 수평 공백(Horizontal whitespace)

언어 또는 다른 스타일 규칙을 넘어, 그리고 리터럴, 주석 Javadoc을 제외하고, 하나의 ASCII space는 다음 상황에서만 적용된다.

 

1. if, for, catch와 같은 예약어를 해당 줄 뒤에 오는 여는 괄호( ( ) 괄호로부터 분리한다.

 

2. else, catch와 같은 예약어를 해당 줄 앞에 오는 닫는 중괄호( } )에서 분리한다.

 

3. 여는 중괄호( { ) 관련 두 가지 예외

  • 예외: @SomeAnnotation({a, b}) (공백 없음)
  • 예외: String [][] x = {{"foo"}} ( {{ 사이에 공백 없음)

4. 이항 또는 삼항 연산자 양쪽에서 사용. 이는 'operator-like' 기호에도 적용된다.

  • 결합 타입 바운드에서 앰퍼샌드( & ) : <T extends Foo & Bar>
  • 다중 예외 처리를 위한 catch 블록 파이프 : catch (FooException | BarException e)
  • 향상된 for 구문의 콜론( : )
  • 람다 표현식의 화살표 : String str) -> str.length()

아래의 경우 공백이 적용되지 않는다.

  • 메서드 레퍼런스의 두 개의 콜론( :: ) : Object::toString
  • '. '을 사용한 분리 : object.toString()

5. ,:; 이후 또는 캐스팅의 닫는 괄호( ) ) 뒤에서.

 

6. 더블 슬래시( // )로 주석 처리를 할 때 더블 슬래 기호에서 한 칸 띄운다. 여러 칸 띄우는 것도 허용되지만, 필요치 않다.

 

7. 타입과 변수 선언 사이에 공백을 넣는다. List <String> list

 

8. (선택사항) 배열 초기화 시 중괄호는 다음과 같이 공백을 허용한다. 

new int[] {5, 6} // Okay
new int[] { 5, 6 } // Okay

 

9. 타입 어노테이션이나 대괄호( [] ) 사이.

 

4.6.3 수평 정렬 : 절대 요구되지 않음

용어 관련 참고 : 수평 정렬은 이전 라인의 코드 길이를 맞추기 위해 이전 라인의 코드 바로 아래에서 특정 토큰을  코드에 여러 줄의 공백을 추가하는 것이다.

 

이 방법이 금지된 것은 아니지만 Google Style 가이드에서 권장하지 않는다. 

 

private int x; // this is fine
private Color color; // this too

private int   x;      // permitted, but future edits
private Color color;  // may leave it unaligned

 

Tip: 정렬은 가독성에 도움이 되지만, 유지보수에 문제가 될 수 있다. 만약 소스코드 한 줄이 변경되었고, 이것이 이전 소스코드 형식을 망가뜨렸다고 생각해보자. 코드 작성법 상 허용되지만 유지보수자 입장에서는 좋지 않다. 또한 정렬 때문에 개발자가 코드 근처의 공백을 조정해야 하고, 이와 관련된 모든 코드를 연속적으로 재구성해야 하는 문제가 발생할 수 있다. 코드 한 줄의 변경은 일종의 폭발 반경(blast radius)을 가지고 있다. 하나의 변경이 생산성에 저하를 초래할 수 있고, 버전 관리의 복잡도 증가, 코드 가독성 저하로 협업 속도 저하 등의 문제로 이어질 수 있다.

 

4.7 괄호 그룹핑 : 추천

선택적 괄호 그룹핑은 저자와 리뷰어가 코드가 잘못 해석될 여지가 없고, 코드 자체의 가독성이 좋다고 동의한 경우에 생략될 수 있다. 코드를 읽는 모든 사람들이 자바 연산자 우선순위 테이블을 기억하고 있다고 짐작하지 말자.

 

4.8 특정 구조

 

4.8.1 Enum 클래스

 

Enum 상수 뒤의 콤마( , ) 이후에 줄 바꿈 하는 것은 선택사항이다. 여러 번 줄 바꿈 해도 상관없다(일반적으로 한 번 한다.)

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

  NO, // Enum 상수 뒤의 콤마( , ) 이후에 줄바꿈 하는 것은 선택사항이다. 
  MAYBE
}

 

메서드나 관련 문서가 없는 enum 클래스는 배열 초기자 형태를 가질 수 있다.(4.8.3.1의 배열 초기자 파트 확인)

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

 

enum 클래스도 클래스이기 때문에 클래스 포맷팅 형식을 적용할 수 있다.

 

4.8.2 변수 선언

 

4.8.2.1 한 번에 하나의 변수 선언

모든 배열 선언은 하나의 배열만 생성한다. 'int a, b;' 이렇게 한 번의 선언으로 여러 개의 변수를 동시에 생성하지 않는다.

 

예외: for 반복문에서는 여러 개의 변수 생성이 허용된다.( for(int a, b = 1; ; ;) )

 

4.8.2.2 

지역 변수는 반드시 변수를 포함하는 블록이나 block-like construct의 시작점에서 선언되지 않는다. 

지역 변수는 변수 사용 범위를 최소화하기 위해서 지역 변수가 사용되는 근처 지점에 선언된다. 

일반적으로 지역 변수 선언과 동시에 초기화를 하거나 변수 선언 이후 초기화를 한다.

// 아마도 이런 모습이 아닐까.

// 변수 선언과 동시에 초기화
int a = 1;

// 변수 선언 이후 초기화
int a;
a = 1;

 

4.8.3 배열

 

4.8.3.1 배열 초기화 : 'block-like'와 같이

배열을 초기화할 때 block-like construct 형태로 포맷팅 할 수 있다. 

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-style 배열 선언은 하지 않는다.

대괄호( [] )는 타입 옆에 붙는다. 변수 옆에 붙지 않는다.

String[] args, not String args[]

 

4.8.4 Switch 구문

용어 관련 참고 : Switch문 블록 중괄호( {} ) 내부에는 하나 이상의 구문이 있다. 각 구문 그룹은 하나 이상의 switch labels로 구성되어 있다.(case 또는 default) switch labels 이후 하나 이상의 구문이 이어진다.

 

4.8.4.1 들여 쓰기

다른 블록과 마찬가지로 switch 블록 또한 +2 만큼 들여 쓰기 한다. 

 

switch label 이후 줄 바꿈이 되고 블록이 열린 것처럼 +2 만큼 들여 쓰기가 된다. 뒤이어 작성되는 switch label은 블록이 닫힌 것처럼 이전의 들여 쓰기 레벨을 따른다. 

 

4.8.4.2 실패(Fall-through) : 주석

switch 블록 내부에서 각 구문을 종료하거나(break, continue, return, exception), 특정 실행을 수행하도록 할 수 있다. 실패를 알려줄 수 있는 어떠한 주석도 괜찮다.(일반적으로 // fall through라고 한다) 이 주석은 switch 블록의 마지막 구문에 오지는 않는다.

switch (input) {
  case 1:
  case 2:
    prepareOneOrTwo();
    // fall through
  case 3:
    handleOneTwoOrThree();
    break;
  default:
    handleLargeNumber(input);
}

case 1: 다음에 주석을 다는 것은 불필요하다. 구문 마지막에 주석을 추가하자.

 

4.8.4.3 default 구문

각 switch 구문은 default 구문을 가진다.(default 구문이 코드가 있는지 없는지의 여부와는 상관없이)

 

예외: enum 타입의 switch 구문에서는 만약 모든 가능한 종류의 값을 포함한 case가 존재할 경우 default 구문이 생략될 수 있다.

 

4.8.5 어노테이션

어노테이션은 documentation block 이후 클래스, 메서드, 생성자에 적용된다. 어노테이션은 한 줄에 하나씩 작성한다. 여기서의 줄 바꿈은 4.5 섹션과 같은 줄 바꿈이 아니기 때문에 들여 쓰기 레벨이 증가하지 않는다. 

@Override
@Nullable
public String getNameIfPresent() { ... }

 

예외: 파라미터 없이 하나의 어노테이션만 사용하는 경우 다른 코드와 한 줄에 같이 작성할 수 있다.

@Override public int hashCode() { ... }

 

필드에 적용되는 어노테이션은 documentation block 바로 뒤에 위치한다. 이 경우 복수의 어노테이션은 같은 줄에 위치한다. 

@Partial @Mock DataLoader loader;

 

파라미터, 지역 변수 또는 타입 어노테이션 포맷팅에 대한 세부 규칙은 없다.

 

4.8.6 주석

이 섹션은 주석 실행에 대해 언급한다. 

 

모든 줄 바꿈 앞에 임의의 공백이 있고, 뒤이어 구현 주석(an implementation comment)이 온다. 이런 주석은 그 줄이 공백이 없도록(non-blank) 만든다.

 

4.8.6.1 블록 주석 스타일

블록 주석은 주변 코드와 같은 들여 쓰기 레벨을 가진다. 주석 형태는 /*... */ 또는 //...이다. 

/*... */ 형태로 주석을 사용할 때는 * 마크로 주석이 시작된다.

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

 

별포나 다른 문자로 그려진 박스 안에 주석이 위치하지 않는다.

 

Tip: 여러 줄의 주석을 작성할 때 자동 코드 포매터가 라인을 re-wrap 하도록 하고 싶다면 /*... */ 형태를 사용하자. 

//... 형태의 주석에서 re-wrap이 적용되지 않는다.

 

4.8.7 접근제어자

클래스나 멤버 접근 제어자는 Java Language Specification에 정의된 방식에 따라 작성된다.

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

 

4.8.8 숫자 리터럴

long 형태의 정수 리터럴은 대문자 L을 접미사로 사용한다. 숫자 1과 구별하기 위한 목적이므로 절대 소문자를 사용하지 않는다. 

3000000000L

 

5 네이밍

 

5.1 모든 식별자에 적용되는 공통 규칙

식별자는 ASCII 문자와 숫자만 사용한다. 소수인 경우 언더스코어를 사용한다. 유효한 식별자 이름은 정규식과 일치한다.

 

Google Style에서는 특수 접미사나 접두사를 사용하지 않는다. name_, mName, s_name, kName과 같은 이름은 Google Style이 아니다.

 

5.2 식별자 타입 규칙

 

5.2.1 패키지 이름

패키지 이름은 여러 소문자 형태의 글자들이 함께 결합되어 있는 형태를 가진다.

// 올바른 패키지 이름
com.example.deepspace

// 잘못된 패키지 이름
com.example.deepSpace
com.example.deep_space

 

5.2.2 클래스 이름

클래스 이름은 UpperCamelCase 형태로 작성한다.

 

일반적으로 클래스 이름은 명사 형태로 작성한다.(Character, ImmutableList) 인터페이스 이름 또한 명사 형태로 작성한다.(List) 하지만 형용사 형태를 가지기도 한다.(Readable)

 

테스트 클래스는 테스트하는 클래스 이름 뒤에 'Test'를 붙이는 형식으로 이름을 짓는다.(HashTest or HashIntegrationTest)

 

5.2.3 메서드 이름

메서드 이름은 lowerCamelCase 형태를 가진다.

 

메소드 이름은 동사 형태로 작성한다.(sendMessage, stop)

 

논리적 컴포넌트를 구분하기 위해 JUnit 테스트에서 사용되는 메소드 이름에는 언더스코어가 나올 수 있다. 각 컴포넌트는 lowerCamelCase 형태로 작성된다. 

 

한 가지 전형적인 패턴은 <methodUnderTest>_<state>이다.(pop_emptyStack)

테스트 메서드 네이밍에 대한 명확한 규칙은 없다.

 

5.2.4 상수 이름

상수 이름은 CONSTANT_CASE를 사용한다. 

CONSTANT_CASE : 대문자로만 이루어짐. 각 단어는 언더스코어(_)로 연결된다. 

 

상수(Constants)는 그 내용이 불변하고 메서드가 의도하지 않은 값(side effect)을 출력하지 않는 정적 final 필드이다. 원시 값, 문자열, 불변 타입, 불변 타입의 불변 컬렉션을 포합 한다. 만약 어떤 인스턴스의 상태가 변경 가능하다면 그것은 상수가 아니다. 하지만 객체 변형을 막는 용도로만 사용되지는 않는다.

// 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 키워드 없음
final String nonStatic = "non-static"; // static 키워드 없음
static final Set<String> mutableCollection = new HashSet<String>(); // 가변타입 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 상수가 아닌 필드 이름

상수가 아닌 필드 이름(static 또는 기타)은 lowerCamelCase 형태를 가진다.

일반적으로 이름은 명사나 명사구로 작성한다.(computedValues, Index)

 

5.2.6 파라미터 이름

파라미터 이름은 lowerCamelCase 형태를 가진다.

public 메서드에서 하나의 문자로 이루어진 파라미터의 사용은 되도록 피하자.

 

5.2.7 지역 변수 이름

지역 변수 이름은 lowerCamelCase 형태를 가진다.

final이나 불변인 경우에도 지역 변수는 상수로 취급되지 않는다. 그리고 상수의 형태로 지역 변수를 생성하면 안 된다.

 

5.2.8 타입 변수 이름

각 타입 변수는 둘 중 하나의 형태로 네이밍 된다.

  • 하나의 대문자 형태이거나 하나의 대문자와 하나의 숫자가 합쳐진 형태.(E, T, X, T2)
  • 클래스 네이밍 형태(섹션 5.2.2 참고)를 가지면서 끝에 대분자 하나를 붙이는 형태.(RequestT, FooBarT)

 

5.3 카멜 케이스

약어나 IPv6, iOS 같은 특이한 구조가 있는 경우와 같이 영어 구문을 카멜 케이스로 변환하는 합리적인 방법인 하나 이상 있을 수 있다. 예측 가능성을 높이기 위해 Google Style에서는 다음과 같은 결정적인 체계를 명시하고 있다.

 

산문(prose) 형태의 이름으로 시작한다.

 

1. 구문을 순수 ASCII로 변환하고 아포스트로피(')를 모두 제거한다.( "Kim's algorithm" -> "Kims algorithm" )

2. 1의 결과를 단어로 쪼갠다. 그리고 공백과 나머지 구두점으로 분할한다(일반적으로 하이픈 - 사용한다)

  • 추천: 단어가 이미 일반적인 카멜 케이스 형태를 가지고 있다면, 단어를 각개의 형태로 분리한다("AdWords" -> "ad words") 참고할 점은, "iOS'와 같은 단어는 그 자체로 카멜 케이스가 아니다. 모든 관습을 무시하기 때문이다. 따라서 이 단어의 경우 앞서 언급한 추천 사항이 적용되지 않는다.

3. 모두 소문자 형태로 바꾼다.(약어도 포함) 그 뒤 첫 글자를 대문자로 한다.

  • 각 단어의 첫 글자는 대문자로.
  • 각 단어의 첫 글자를 제외한 나머지는 소문자로.

4. 모든 글자를 하나의 식별자 형태로 합친다.

 

참고로 원래 단어의 대소문자 형태를 무시한다. 

가능은 하지만, 추천하지 않는다.

 

어떤 단어는 모호하게 하이픈으로 연결되어 있다. 
"nonempty"와 "non-empty" 모두 맞다. 따라서 메소드 이름 checkNonempty 그리고 checkNonEmpty 모두 맞다.

 

6 프로그래밍 습관

 

6.1 @Override : 항상 사용됨

@Override는 사용되는 메서드마다 붙인다. 이는 클래스가 슈퍼 클래스 메서드를 오버 라이딩(재정의) 하고, 클래스 메서드가 인터페이스 메서드를 구현하고 있고, 인터페이스 메서드가 슈퍼인터페이스 메소드를 재구현하고 있음을 나타낸다.

 

예외: @Override 부모 메소드가 @Deprecated 되었을 경우 생략될 수 있다.

 

6.2 catch 된 예외 : 무시하지 않는다.

아래의 경우를 제외하고 catch 된 예외에 대한 응답으로 아무것도 하지 않는 경우는 드물다.(일반적인 응답으로는 로그를 남기거나, 불가능하다고 판단되는 경우 AssertionError를 rethrow 한다)

 

catch 블록에서 아무것도 하지 않는 것이 최선이라고 판단되는 경우, 주석을 통해 해당 사유를 남기자.

try {
  int i = Integer.parseInt(response);
  return handleNumericResponse(i);
} catch (NumberFormatException ok) {
  // it's not numeric; that's fine, just continue
}
return handleTextResponse(response);

 

예외: 테스트에서 만약 이름이 expected이거나 expected로 시작한다면 catch 된 예외는 아무런 주석처리 없이 넘어갈 수 있다. 아래 코드는 expected 타입의 예외가 발생할 것이라고 확실시되는 코드를 처리하는 일반적인 방식이다. 코드에서 알 수 있듯이 주석은 불필요하다.

try {
  emptyStack.pop();
  fail();
} catch (NoSuchElementException expected) {
}

 

6.3 정적 멤버 : 클래스 사용할 수 있음

클래스 멤버 참조에 대한 자격이 필요한 경우, 해당 클래스의 이름으로 자격이 부여될 수 있다. 해당 클래스 유형의 참조나 표현식으로 자격이 부여되지 않는다.

클래스 멤버를 참조할 때 해당 클래스 이름을 사용하자.

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

 

6.4 Finalizers : 사용되지 않는다.

Object.finalize를 재정의 하는 것은 아주 드물다.

 

7 Javadoc

 

7.1 Formatting

 

7.1.1 일반적인 형태

Javadoc 블록의 기본 형태 구성은 아래 예시와 같다.

/**
 * Multiple lines of Javadoc text are written here,
 * wrapped normally...
 */
public int method(String p1) { ... }

 

한 줄(single-line) 형태는 Javadoc 블록이 한 줄 형태로 표현 가능할 경우 한 줄 형태로 대신할 수 있다. 이는 @return과 같은 블록 태그가 없을 때만 적용 가능하다.

 

7.1.2 Paragraphs

하나의 빈 줄 - 즉 정렬된 선행 별표( * )만 포함한 줄은 단락 사이와 블록 태그 그룹 뒤에 위치한다.

첫 번째 단락을 제외한 각 단락은 첫 번째 단어 바로 앞에 뒤에 공백이 없는 <p>가 있다.

 

7.1.3 블록 태그

표준 블록 태그는 @param, @return, @throws, @deprecated 순서로 표시된다. 빈 서술에 사용되지 않는다.(혼자 단독으로 사용되지 않는다) 

블록 태그가 한 줄에 맞지 않는 경우 @ 위치에서 다음 줄을 4번(또는 그 이상) 들여 쓰기 한다.

 

7.2 The summary fragment

Javadoc 블록은 간략한 summary fragment를 가지고 시작한다. 이 fragment는 매우 중요하다. 이것은 클래스와 메서드 인덱스와 같은 특정 콘텍스트를 나타내는 텍스트의 유일한 부분이다. 

 

fragment는 완전한 문장이 아니라 명사구 또는 동사구이다. A {@code Foo} is a... 또는 This method returns...로 시작하지 않으며 Save the record.. 와 같은 완전한 명령형 문장을 형성하지도 않는다. 하지만 fragment는 대문자이며 문장을 끝마치는 마침표를 가진다.

 

Tip: Javadoc 작성 시 흔한 실수는 /** @return the customer ID */ 이렇게 작성하는 것이다. /** Returns the customer ID. */ 이렇게 작성해야 한다.

 

7.3 Javadoc이 사용되는 곳

최소한 Javadoc은 모든 public 클래스와 그러한 클래스의 모든 public, protected 멤버에 사용된다. 

아래에 몇 가지 예외가 있다.

 

섹션 7.3.4에서 설명하는 것처럼 추가 Javadoc 콘텐츠가 존재할 수도 있다.

 

7.3.1 예외 : self-explanatory methods

getFoo와 같은 단순하고 명확한 메서드인 경우 Javadoc을 사용해도 되고 안 해도 된다. 단순히 'foo'를 반환하는 것 이외의 다른 동작을 하지 않는 경우라면 굳이 Javadoc을 사용하지 않아도 된다.

 

중요: 사용자가 알아야 할 정보를 생략하는 것을 정당화하기 위해 이 예외를 인용하는 것을 적절하지 않다. 예를 들어 getCanonicalName 메서드가 있는데, 이 메서드는 ( /** Returns the canonical name. */ 이 무엇이고 왜 존재하는지 설명하는 내용이 포함되었을) documentation을 생략하면 안 된다. 만약 일반 사용자가 'canonical name'이라는 용어가 무슨 의미를 가지는지 알 수 없다면 큰 낭패일 것이다.

 

7.3.2 Exception : overrides

Javadoc은 슈퍼 타입 메서드를 오버라이드 한 메서드를 항상 주석 처리하지 않는다.

 

7.3.4 Non-required Javadoc

다른 클래스와 멤버는 필요에 따라 또는 원할 경우 Javadoc을 가진다.

 

구현 주석이 클래스나 멤버의 전체 목적이나 동작을 정의한다면 해당 주석은 Javadoc으로 작성된다.(/** 사용)

 

필수가 아닌 Javadoc은 섹션 7.1.2, 7.1.3, 7.2의 포매팅 규칙을 엄격히 준수할 필요가 없다. 물론 이 규칙들을 따르면 좋기는 하다.

반응형