[JAVA] Google Java Style Guide 요약 번역

알린의 개발노트·2021년 11월 23일
0

이 글은 Google Java Style Guide의 내용을 번역하여 핵심만 정리한 글이다.

이후 본문 전체에 대한 번역을 추가하며 수정해 나갈 예정이다.


목차

펼치기/접기

1 소개

이 문서는 구글의 Java 코딩 스타일을 정의한 문서이다.

1.1 용어 설명

1.2 가이드 노트


2 소스 파일의 기본

2.1 파일명

소스 파일은 클래스를 포함하는 이름으로 되어있고 .java 확장자를 가진다.

2.2 파일 인코딩: UTF-8

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

2.3 특수 문자

2.3.1 공백 문자

ASCII 가로 공백 문자(0x20)가 유일한 공백 문자

  • 이외 모든 공백 문자는 이스케이프됨
  • 탭 문자는 들여쓰기에 사용되지 않음

2.3.2 특수 이스케이프 시퀀스

특수 이스케이프 시퀀스는 다음을 사용한다. (\b\t\n\f\r\"\' and \\)

8진수 (e.g. \012) 또는 유니코드 (e.g. \u000a) 등은 쓰지 않는다.

2.3.3 비 ASCII 문자

비 아스키 문자의 경우 유니코드 (e.g. ) 또는 유니코드 이스케이프 (e.g. \u221e) 등이 있다.

유니코드를 사용하는것이 좋지는 않지만 가독성이 좋다면 써도 되긴 한다.

Examples:
가장 좋은 경우
String unitAbbrev = "μs";
return '\ufeff' + content; // byte order mark
허용되지만 권장되지는 않음
String unitAbbrev = "\u03bcs"; // "μs"
String unitAbbrev = "\u03bcs"; // Greek letter mu, "s"
안됨
String unitAbbrev = "\u03bcs";


3 소스 파일 구조

소스 파일은 다음 순서대로 작성해야함:

  1. 라이선스 또는 저작권 정보(있는 경우)
  2. Package 문
  3. import 문
  4. class 문

각 섹션은 하나의 빈 줄로 구분


* 예시 코드

기존의 코드 스타일

package com.ozragwort.moaon.springboot.service.videos;

import com.ozragwort.moaon.springboot.domain.categories.Categories;
import com.ozragwort.moaon.springboot.domain.categories.CategoriesRepository;
import com.ozragwort.moaon.springboot.domain.channels.Channels;
import com.ozragwort.moaon.springboot.domain.channels.ChannelsRepository;
import com.ozragwort.moaon.springboot.domain.videos.*;
import com.ozragwort.moaon.springboot.dto.videos.*;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.google.common.base.Preconditions.checkArgument;
import static com.ozragwort.moaon.springboot.domain.specs.VideosSpecs.searchWith;
import static com.ozragwort.moaon.springboot.domain.specs.VideosSpecs.VideosSearchKey;
import static com.ozragwort.moaon.springboot.util.Calculation.calcScore;
import static com.ozragwort.moaon.springboot.util.ConvertTo.DurationStringToSecond;
import static com.ozragwort.moaon.springboot.util.ConvertTo.StringToUTCDateTime;
import static java.util.Objects.isNull;

google java code style로 수정된 코드 스타일

/*
 * 저작권이 있는 경우 작성
 */

package com.ozragwort.moaon.springboot.service.videos;

import static com.ozragwort.moaon.springboot.domain.specs.VideosSpecs.searchWith;
import static com.ozragwort.moaon.springboot.util.Calculation.calcScore;
import static com.ozragwort.moaon.springboot.util.ConvertTo.DurationStringToSecond;
import static com.ozragwort.moaon.springboot.util.ConvertTo.StringToUTCDateTime;
import static java.util.Objects.isNull;
import static org.h2.mvstore.DataUtils.checkArgument;

import com.ozragwort.moaon.springboot.domain.categories.Categories;
import com.ozragwort.moaon.springboot.domain.categories.CategoriesRepository;
import com.ozragwort.moaon.springboot.domain.channels.Channels;
import com.ozragwort.moaon.springboot.domain.channels.ChannelsRepository;
import com.ozragwort.moaon.springboot.domain.specs.VideosSpecs.VideosSearchKey;
import com.ozragwort.moaon.springboot.domain.videos.*;
import com.ozragwort.moaon.springboot.dto.videos.*;
import java.util.*;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

3.1 라이선스 또는 저작권 정보

라이선스 또는 저작권 정보가 있는 경우 추가한다.

3.2 Package 문

패키지 문은 줄바꿈 하지 않고 작성한다. (아무리 길어도 한줄로 쓰라는 의미)

3.3 Import 문

3.3.1 와일드카드 import를 하지않음 (No wildcard imports)

static 또는 기타의 와일드카드 import는 하지 않는다.

3.3.2 줄바꿈 금지 (No line-wrapping)

아무리 길어도 한줄로 쓰라는 의미이다.

3.3.3 순서와 간격 (Ordering and spacing)

다음 순서대로 import 해야한다.

  1. static import를 한 블록에 추가함
  2. non-static import를 한블록에 추가함

두 블록 사이는 하나의 빈 줄로 구분한다.
각 블록내에서는 ASCII 정렬로 순서를 정한다.

3.3.4 static import를 하지않음 (No static import for classes)

클래스에는 static import를 하지 않는다.

3.4 클래스 선언 (Class declaration)

3.4.1 하나의 최상위 클래스 선언(Exactly one top-level class declaration)

1 JAVA 파일 1 Class → 하나의 java 파일엔 하나의 Class만 만들자.

3.4.2 class 내용의 순서

클래스의 멤버의 순서는 딱 정해진것은 없지만 논리적인 순서가 있어야 한다.

단순하게 새로운 메서드를 만들때 가장 뒤에 만드는 식으로 만들면 안된다는 의미이다.

3.4.2.1 Overloads: 분할하지 않음

여러 생성자, 같은 이름을 가진 메서드 들은 꼭 모아서 순차적으로 나타낸다.

private도 포함해야한다.


4 포맷팅 (Formatting)

4.1 중괄호 (Braces)

4.1.1 생략 가능하더라도 중괄호를 사용해야함

예를들어 ifelsefordo and while에서 내용이 비어있거나 한줄이어도 중괄호를 꼭 사용함

틀린 예시

if (sum > 0)
    return sum;
else
    return -1;

옳은 예시

if (sum > 0) {
    return sum;
}
else {
    return -1;
}

4.1.2 블록이 비어있지 않는 경우 : K&R 스타일

Kernighan & Ritchie 스타일

  • 여는 중괄호 앞 : 줄 바꿈을 하지않음
  • 여는 중괄호 뒤 : 줄 바꿈을 함
  • 닫는 중괄호 앞 : 줄 바꿈을 함
  • 닫는 중괄호 뒤 : 줄 바꿈을 함
    • 메서드 또는 클래스의 끝, else닫는 경우 중괄호 뒤 줄 바꿈이 없을 수 있음

Examples:

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();
    }
  }
};

4.1.3 빈 블록인 경우

그냥 중괄호를 열고 닫아도 됨.

Examples:

// 됨
void doNothing() {}

// 됨
void doNothingElse() {
}

// 안됨 (try에는 내용이 있기 때문에)
try {
    doSomething();
} catch (Exception e) {}

4.2 블록 들여쓰기 : +2 spaces

새 블록에 진입할 때 마다 2칸의 space로 들여쓰기를 추가함

4.3 한 줄에 하나의 명령문

명령문 뒤에 줄 바꿈

4.4 열 길이 제한: 100

한 줄에 길이가 100이 넘으면 안됨.

길이의 기준은 모든 유니코드 코드 포인트를 의미함

Exceptions:

  • 열 제한을 할 수 없는 행(Javadoc의 긴 URL, JSNI 메소드 등)
  • package, import
  • 쉘에서 자를수 없는 주석 명령줄

4.5 줄 바꿈 (Line-wrapping)

한줄의 코드를 여러줄로 나누는 활동을 말한다. 쉽게 말해 엔터치는걸 말한다.

모든 상황에서 정확한 공식은 없지만 여러 방법이 있다.

4.5.1 언제 줄 바꿈을 할까 (Where to break)

줄 바꿈을 하게되는 경우는 다음과 같다.

  • 기호 앞에서
    • the dot separator (.)
    • the two colons of a method reference (::)
    • an ampersand in a type bound (<T extends Foo & Bar>)
    • a pipe in a catch block (catch (FooException | BarException e)).
  • 대입 연산자 앞에서
    new StringBuilder()
    .append("a")
    .append("b");
    
    "a"
    + "b";
  • 메서드, 생성자 이름에서 (뒤에서
    StringTokenizer stk = new StringTokenizer(
                                br.readLine());
  • 쉼표 (,) 뒤에서
    public Videos(Channels channels,
                      String videoId,
                      String videoName,
                      String videoThumbnail,
                      String videoDescription,
                      LocalDateTime videoPublishedDate,
                      long videoDuration,
                      int viewCount,
                      int likeCount,
                      int dislikeCount,
                      int commentCount,
                      double score,
                      List<String> tags) {
    }
  • 람다식에서 중괄호가 없는 단일 표현식에서 화살표 바로 뒤에 중단이 오는 경우를 제외한 경우 화살표 뒤에서 가능
    MyLambda<String, Long, Object> lambda =
        (String label, Long value, Object obj) -> {
            ...
        };
    
    Predicate<String> predicate = str ->
        longExpressionInvolving(str);
    

4.5.2 연속 줄 바꿈은 들여쓰기 +4 spaces

줄 바꿈 시 첫 번째 이후 들여쓰기는 최소 +4 space로 들여쓰기한다.

4.6 공백

4.6.1 세로 공백

다음의 경우 한 줄의 세로 공백을 나타낸다.

  1. 클래스 멤버를 구분할 때: 메소드, 생성자, 멤버 변수 등 사이
    멤버 변수의 경우 사이에 코드가 없으면 공백이 없어도 됨
  2. 메소드 내부에서 논리적으로 그룹핑 되는 경우

4.6.2 가로 공백

  1. iffor or catch, 뒤에 오는 (() 사이에 공백
  2. else or catch, 와 그 앞에 있는 (}) 사이에 공백
  3. 여는 중괄호 ({), 앞에 공백 예외 두 가지:
    • @SomeAnnotation({a, b}) (공간을 사용하지 않음)
    • String[][] x = {{"foo"}}; (공백이 필요하지 않음{{, )
  4. 이항, 삼항 연산자, operator-like 양쪽에 사용
    • 타입 바운딩의 & : <T extends Foo & Bar>
    • 예외 처리 시 | : catch (FooException | BarException e)
    • 향상된 for 문 에서 (for (int n : list))
    • 람다에서 화살표 ->: (String str) -> str.length()
      • 다음은 공백을 넣지 않음
    • 두개의 세미콜론 (::) : Object::toString
    • 하나의 점 (.) :object.toString()
  5. ,:; 또는 타입 변경 cast 시 ()) 뒤에 공백
  6. 주석에서 (//) 뒤 공백. 여러개가 가능하지만 필수는 아님
  7. 변수 선언 시 타입과 변수명 사이 공백: List<String> list
  8. 배열 선언문 사이의 공백
    • new int[] {5, 6} and new int[] { 5, 6 } 둘 다 사용 가능
  9. type annotation and 대괄호 ([] )or (...).

4.6.3 가로 정렬 : 필요없음

가독성을 위해 공백을 이용하여 정렬을 하는 경우가 있지만 google 스타일에선 요구되지 않음

private int x; // 괜찮음
private Color color; // 괜찮음

private int   x;      // 해도 되지만 후에 수정해야함
private Color color;  // 괜찮음

4.7 그룹화 괄호 : 권장

코드 작성자와 검토자가 합의한 경우에만 생략된다.

java의 연산자 우선 순위 테이블을 가지고 있다고 해도 가능하면 써야한다.

예를들면 int n = 5 * 3 + 1 로 써도 되지만 가능하면 int n = (5 * 3) + 1로 쓰라는 말이다.

4.8 특별한 구조

4.8.1 Enum 클래스

enum 상수의 각 컴마 다음에 개행은 선택적이다. 추가의 개행도 허용된다. (보통 한번):

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

  NO,
  MAYBE
}

별다른 documentation이 없는경우 배열 초기화와 같은 포맷으로 작성해도 된다. (4.8.3.1)

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

4.8.2 변수 선언

4.8.2.1 한줄에 하나씩 선언

int a, b; 이렇게 하지 말라는 의미이다.

하지만 for 문의 헤더에서는 여러 변수 선언이 쓰일 수 있다.

4.8.2.2 필요할 때 선언

꼭 블럭이 시작될 때 변수를 선언하지 않아도 된다는 의미이다.

하지만 지역 변수는 그 변수가 사용될 곳에서 최대한 가까이 선언하고 선언과 동시에 초기화를 시킨다.

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언어 처럼 선언하지 않음

String[] args처럼 선언할 수 있고, String args[]처럼 선언하지 않는다.

4.8.4 Switch 문

4.8.4.1 들여쓰기

다른 코드처럼 space +2로 한다.

4.8.4.2 실패 또는 지나감

switch문은 breakcontinuereturn 과 같이 switch문을 종료시킬 수 있다. 이런 경우가 아닌 경우 다름 구문을 실행하게 되는데 이 때 주석을 사용할 수 있다. 또한 이 주석은 해당 case의 마지막에 온다. case 1에는 쓰지 않고 case 2에서 한번에 주석을 달면 된다.

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

4.8.4.3 default 를 포함

default 에서 아무 코드가 없더라도 포함하여 코드를 작성한다.

4.8.5 어노테이션 (Annotations)

클래스, 메서드, 생성자에 적용되는 어노테이션은 documentation 블럭 바로 이후에 나열된다.

Example:

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

Exception: 파라미터가 없는 단일 어노테이션은 한줄에 쓸 수 있다.

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

필드에서 사용되는 어노테이션은 한줄에 쓸 수 있다.

@Partial @Mock DataLoader loader;

4.8.6 주석 (Comments)

이 부분은 단순 주석이다.

Javadoc은 7절에 있다.

4.8.6.1 블럭 주석 스타일

/* ... */ , // ... 을 사용할 수 있다. 단 여러 줄의 /* ... */ 를 사용하면 두 번째 줄 이후의 * 는 이전 줄의 * 과 정령되어야 한다.

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

4.8.7 접근 제한자 (Modifiers)

사용 가능한 클래스와 멤버의 접근 제한자는 다음과 같다. :

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

4.8.8 숫자 리터럴

long형 숫자의 경우 3000000000l대신 3000000000L 으로 쓴다.

소문자 l은 숫자 1과 헷갈릴 수 있기 때문이다.


5 네이밍 (Naming)

5.1 모든 식별자에 대한 공통 규칙

식별자는 ASCII 숫자와 문자만을 사용한다. 일부의 경우 _를 쓰기도 한다.

하지만 google style에서는 특별한 접미사나 접두사는 쓰지 않는다 : name_mNames_name and kName.는 쓰지않는다.

5.2 식별자 타입에 대한 규칙

5.2.1 패키지 이름

패키지 이름은 모두 소문자이며 연속된 단어는 단순히 함께 연결된다.

com.example.deepspace로 쓰고 com.example.deepSpace or com.example.deep_space로 쓰지 않는다.

5.2.2 클래스 이름

  • 클래스 이름은 "UpperCamelCase"로 작성한다.

  • 주석의 경우 정해진 규칙은 없다.

  • 테스트 클래스의 이름은 해당 클래스의 뒤에 Test를 추가한다.

  • naming convention의 네 가지 종류

    1. UpperCamelCase : 띄어쓰기 부분을 모두 대문자로 치환. 가장 앞 문자는 대문자
    2. lowerCamelCase : 띄어쓰기 부분을 모두 대문자로 치환. 가장 앞 문자는 소문자
    3. snakecase : 띄어쓰기 대신 언더바()를 사용하고 모든 문자를 소문자로 치환
    4. CONSTANTCASE : 띄어쓰기 대신 언더바()를 사용하고 모든 문자를 대문자로 치환

5.2.3 메서드 이름

메서드 이름은 "lowerCamelCase"를 사용한다.

5.2.4 상수 이름

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

상수는 내용이 변경되지 않아야 한다. 따라서 상태가 바뀐다면 상수가 아니다.

// 상수
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 }

// 상수가 아님
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 상수가 아닌 필드 이름

"lowerCamelCase"를 사용한다.

5.2.6 파라미터 이름

"lowerCamelCase"를 사용한다.

5.2.7 지역 변수 이름

"lowerCamelCase"를 사용한다.

5.2.8 유형 변수 이름

  • 단일 대문자 (such as ETXT2)
  • 클래스에 사용되는 형식 뒤에 T 를 합쳐서 사용(examples: RequestTFooBarT).

5.3 카멜 케이스 : 정의

가끔씩 "IPv6" 또는 "iOS"와 같이 비정상적인 형식이 있을 수 있다.

따라서 Google Style은 다음과 같은 체계를 사용한다.

  1. 문장을 일반 적인 ASCII로 변환하고 어퍼스트로피를 지운다.
    "Müller's algorithm" → "Muellers algorithm"
  2. 결과를 남은 공백과 구두점을 기준으로 단어로 나눈다.
    이미 캐멀 케이스면 단어로 나눈다. (AdWords → ad words)
  3. 이제 모두 소문자로 바꾸고 첫 번째 글자만 대문자로 바꾼다.
  4. 모든 단어를 합친다.

Examples:

  • XML HTTP request
    • XmlHttpRequest
  • new customer ID
    • newCustomerId
  • inner stopwatch
    • innerStopwatch
  • supports IPv6 on iOS?
    • supportsIpv6OnIos
  • YouTube importer
    • YouTubeImporter
    • YoutubeImporter - 허용 되지만 권장되지는 않음
  • check non-empty
    • checkNonempty
    • checkNonEmpty

6 프로그래밍 실습

6.1 @Override: 항상 사용

@Override annotation 은 사용 가능하면 늘 붙인다.

예외 : 부모 함수가 @Deprecated가 되면 @Override를 생략할 수 있다.

6.2 예외에서 catch : 생략하지 말 것

다음과 같은 경우가 아닌경우 catch의 내용을 생략하지 말자.

정말 빈칸이어도 되는 경우 주석으로 설명을 써두자.

try {
  int i = Integer.parseInt(response);
  return handleNumericResponse(i);
} catch (NumberFormatException ok) {
  // 숫자가 아니어도 그냥 리턴해도 된다. 
}
return handleTextResponse(response);

Exception: 테스트를 할 때, 예외 발생 시 이름을 expected로 쓰는 경우 주석없이 무시될 수 있다.

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

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

정적 클래스 멤버에 대한 참조는 해당 클래스의 이름을 바로 사용하여 작성해도 된다.

Foo aFoo = ...;
Foo.aStaticMethod(); // 좋음
aFoo.aStaticMethod(); // 나쁨
somethingThatYieldsAFoo().aStaticMethod(); // 아주 나쁨

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) { ... }

한줄 예제:

/** An especially short bit of Javadoc. */

기본 형식은 항상 허용된다.

단 한줄로 작성하는 경우는 @return이 없을 때만 가능하다.

7.1.2 문단(Paragraphs)

문단과 문단 사이에는 하나의 빈 줄이 추가된다. (*)만 포함되는 줄

7.1.3 블럭 태그 (Block tags)

사용되는 표준 블럭 태그는 @param@return@throws@deprecated가 있다. 이 네 가지 유형의 설명은 빈칸으로 두면 안된다. 한 줄이 넘어가면 줄 바꿈을 한 뒤 띄어쓰기 4번 이상을 한다.

7.2 요약

Javadoc은 해당 내용의 간단한 요약으로 시작한다. 클래스 및 메소드에 대해 나타나는 유일한 부분이기 때문이다.

/** @return the customer ID */가 아닌  /** Returns the customer ID. */와 같이 작성해야 한다.

7.3 어디서 Javadoc이 사용되는가

모든 public class , public,protected 에서 예외를 제외하고 가능하면 다 쓰자.

7.3.1 예외 : 너무 당연한 경우

Javadoc은 간단하고 명료한 메소드에 경우 선택적으로 사용해도 된다.

예를들면 getFoo와 같은 경우이다. 이것은 foo를 반환하는것이 너무나 당연하기 때문이다.

7.3.2 예외 : overrides

overrides 어노테이션을 사용하는 경우 작성하지 않아도 된다.

7.3.4 Javadoc이 필요없는경우

구현 구석과 착각하여 사용하는 경우가 있을 수 있는데 (/**를 사용한 경우) 필수가 아닌 Javadoc은 7.1.2, 7.1.3, 7.2와 같은 규칙을 꼭 따라야 하는건 아니지만 권장은 한다.

profile
안녕하세요!

0개의 댓글