구글 자바 스타일 가이드

2023. 9. 28. 18:56Server/Java

기억이 안날 때는 이 글을 참고하자 

3. 소스 파일 구조

소스 파일은 다음 순서로 구성됩니다.

  1. 라이센스 또는 저작권 정보 (있을 경우)
  2. Package 구문
  3. import 명세서
  4. 정확히 하나의 최상위 Class

! 정확히 하나의 빈 줄이 각 섹션을 구분합니다. !

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

라이센스 또는 저작권 정보가 파일에 속하면 여기에 속합니다.

→ Package 구문이 항상 소스코드 최상위에 존재하는 줄 알았으나, 의외였습니다. 제가 아직 큰 규모의 코드를 보지 않아서 눈치 채지 못한 것 같습니다.

3.2 Package 구문

패키지 문은 줄 바꿈되지 않습니다.

열 제한 ( 섹션 4.4 참고, 열 제한 : 100 ) 은 패키지 문에 적용되지 않습니다.

3.3 Import 구문

3.3.1 와일드 카드 가져오기 없음

static 또는 기타 와일드 카드 import 는 사용하지 않습니다.

→ 이 부분은 스터디 멤버중 한명이 의문을 던졌던 부분입니다. 클린코드와 내용이 상충한다는 고민이었습니다.

클린 코드: 긴 import 목록은 읽기에 부담스럽다. 80행에 이르는 import 문으로 모듈 상단을 채우고 싶지는 않으리라.”

하지만 저는 이 의견에는 반대표를 던지고 싶습니다. 당장 이전에 진행했던 프로젝트의 테스트 코드를 작성하는 단계에서 와일드 카드를 사용해 import한 적이 있었습니다. 이유는 모두가 생각하는 ‘귀찮음’ 때문이었고, 메서드 이름은 동일하지만 역할이 다르기 때문에 원하는 결과를 받을 수 없었습니다. 후에는 너무 헷갈린 나머지 import 구문을 전부 지우고 다시 순차적으로 import 하는 방식으로 문제를 해결했습니다. 이러한 소요를 줄이기 위해서라도 그저 심미적 이유로 와일드 카드를 사용하기 보다는, 보다 구체적인 import 를 사용해 명시하는 편이 좋다고 생각합니다.

3.3.2 줄 바꿈 없음

import 문은 줄 바꿈되지 않습니다.

열 제한 (섹션 4.4 참고, 열 제한 100 ) 은 import 문에 적용되지 않습니다.

3.3.3 순서 및 간격

imports 는 다음과 같은 순서로 씁니다.

  • 단일 블록에서 모든 static imports.
  • 단일 블록의 모든 non-static imports.

static imports 와 non-static imports 가 모두있는 경우 하나의 빈 줄이 두 블록을 구분합니다.

Import 문 사이에는 다른 빈 줄이 없습니다!

→ import 사이에는 빈 줄을 놓지 않지만, static import 와 non-static import 사이에는 빈 줄로 구분한다는 뜻으로 이해했습니다.

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

static 중첩 클래스에는 static import 가 사용되지 않습니다.

일반적인 import 를 사용합니다.

→ 클래스에서 static import 를 많이 진행하게 되면, 코드가 복잡해질 수록 어떤 클래스에서 static 멤버가 오는지 알아내기 위해서 소요가 생길 수 있을 것 같습니다.

3.4 클래스 선언

3.4.1 정확히 하나의 최상위 클래스 선언

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

3.4.2 클래스 내용 순서

클래스의 멤버 및 이니셜라이저에 대해 선택한 순서는 이해하는데 큰 영향을 미칠 수 있습니다. 그러나 이를 수행하는 방법에 대한 정해진 방법은 없습니다. 다른 클래스는 다른 방식으로 내용 순서를 정할 수 있습니다.

중요한 것은 각 클래스가 몇가지 논리적 순서를 사용한다는 것입니다. 이 순서는 관리자가 요청하면 설명할 수 있어야 합니다.

→ 클래스 순서는 개발자나 사용자에 따라 각기 다른 방식을 사용할 수 있지만, 그것이 결코 무작위적인 방법이어선 안되고, 설명하기 쉽게, 이해하기 쉽게 등의 논리적 순서를 가져야 한다는 뜻으로 이해했습니다.

3.4.2.1 Overloads : 위치 분할 X

클래스에 여러 생성자 또는 동일한 이름을 가진 여러 메서드가 있는 경우 이들은 사이에 다른 코드 없이 순차적으로 나타나야 한다.

4 Formatting

4.1 괄호

4.1.1 선택 사항인 경우에서도 중괄호가 사용됩니다.

if, else, for, do 및 while문 또는 body가 비어 있거나 단 하나의 문이 포함 된 경우에도 괄호가 쓰인다.

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

괄호는 비어 있지 않은 블록 및 블록 유사 구조에 대해 Kernighan 및 Ritchie 스타일을 따릅니다.

  • 여는 중괄호 앞에 줄 바꿈이 없습니다.
  • 여는 중괄호 뒤의 줄 바꿈.
  • 닫는 중괄호 앞의 줄 바꿈.
  • 닫는 중괄호 뒤의 줄 바꿈. ( 중괄호가 명령문을 종료하거나 메서드, 생성자 또는 명명 된 클래스의 본문을 종료하는 경우)
  • 중괄호 뒤에 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();
    }
  }
};

4.1.3 빈 블록 : 간결 할 수 있음

빈 블록 또는 블록과 유사한 구조는 K & R 스타일 일 수 있습니다.

또는 다중 블록 명령문의 일부가 아닌 경우 ({}) 사이에 문자나 줄 바꿈없이 열린 직후 닫을 수 있습니다.

 	// 허용
  void doNothing() {}

  // 허용
  void doNothingElse() {
  }
  // 허용되지 않음 : 멀티 블럭 구문에서는 간결한 빈 블럭을 사용할 수 없다.
  try {
    doSomething();
  } catch (Exception e) {}

4.2 블록 들여 쓰기 : +2 공백

새 블록 또는 블록과 유사한 구조가 열릴 때마다 들여 쓰기가 두 칸씩 증가합니다. 블록이 끝나면 들여 쓰기는 이전 들여 쓰기 수준으로 돌아갑니다. 들여 쓰기 수준은 블록 전체의 코드와 주석 모두에 적용됩니다.

→ 보통 IDE의 Tab 은 4칸의 들여쓰기 기준으로 작성되고 있고, 현재는 거의 4칸의 들여쓰기를 사용하는 것 같습니다.

4.3 한 줄에 하나의 문

각 문 뒤에는 줄 바꿈이 있습니다.

4.4 열 제한 : 100

Java 코드의 열 제한은 100자 입니다.

“character”는 모든 유니 코드 코드 포인트를 의미합니다.

아래 언급 된 경우를 제외하고 이 제한을 초과하는 모든 줄은 섹션 4.5, 줄 바꿈에 설명된대로 줄 바꿈해야 합니다.

각 유니 코드 코드 포인트는 표시 너비 크기에 상관없이 하나의 문자로 계산됩니다. 예를 들어 fullwidth characters(일본어, 한자, 한글, 숫자) 를 사용하는 경우 이 규칙보다 먼저 줄을 줄 바꿈하도록 선택할 수 있습니다.

예외

  • 열 제한을 따를 수 없는 행 (Javadoc의 긴 URL 또는 긴 JSNI 메소드 참조).
  • package 및 import 문
  • 셸에 복붙되는 주석의 명령 줄

4.5 줄 바꿈

💡 코드를 하나의 줄에서 여러 줄로 나눌 때 이 작업을 ‘줄 바꿈’ 이라고 합니다.

 

모든 상황에서 줄 바꿈하는 방법을 정확히 보여주는 공식은 없습니다.

동일한 코드를 줄 바꿈하는 여러 가지 유효한 방법이 많이 있습니다.

참고: 줄 바꿈의 일반적인 이유는 열 제한을 초과하지 않도록하는 것이지만 실제로 열 제한에 맞는 코드도 작성자의 재량에 따라 줄 바꿈 될 수 있습니다.

💡 Tip: 메서드 또는 지역 변수를 추출하면 줄 바꿈 없이 문제를 해결할 수 있습니다.

 

→ 우리는 OOP를 사용하는 만큼, 기능들을 분리하여 가독성 좋은 코드를 작성하는 법을 배워야 하는 이유라고 생각하였습니다.

4.5.1 어디서 줄 바꿈 하는가?

줄 바꿈의 주요 지침은 더 높은 문법 수준에서 중단하는 것 입니다.

also:

  • 비 할당 연산자 에서 줄이 끊어지면 기호 앞에 끊어집니다.
  • 점 구분 기호( . )
  • 메서드 참조의 두 콜론(::)
  • Type 바운드의 앰퍼센드 (<T extends Foo & Bar>)
  • catch 블록의 파이프 (catch (FooException | BarException e))
  • 할당 연산자에서 줄이 끊어지면 일반적으로 기호 뒤에 끊어지지만 어느 쪽이든 혀용됩니다.→ 더 가독성이 좋은 형태로 작성하면 될 것 같습니다.
  • //가능 String a = "foo" + "salt" + "exception" + "bar"; //가능 String a = "foo" + "salt" + "exception" + "bar";
  • 이는 확장 for(foreach) 문 에서 “assignment-operator-like” 콜론에도 적용됩니다.
  • 메서드 또는 생성자 이름은 그 뒤에 오는 여는 괄호가 있을 때( ()까지 쓰고 줄 바꿈)
  • 쉼표 (,) 는 그 앞에 있는 토큰에 연결되어 있을 떄
  • lambda 의 본문이 중괄호가 없는 단일 식으로 구성된 경우 화살표 바로 뒤에 줄 바꿈이 있을 수 있습니다. 이를 제외하고는 lambda의 화살표 옆에서 줄이 끊어지지 않습니다.
  • 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.3 은 특정 토큰을 이전 행과 정렬하기 위해 가변 수의 공백을 사용하는 권장되지 않는 관행을 다룹니다.

4.6 공백

4.6.1 세로 공백

하나의 빈 줄은 이럴 때 나타난다 :

  • 연속적인 멤버 또는 클래스의 초기화 : 필드, 생성자, 메소드, 중첩 클래스, 정적 초기화 그리고 인스턴스 초기화
  • 예외 : 두 개의 연속 된 필드 사이에 다른 코드가 없는 빈 줄은 선택사항입니다. 이러한 빈 줄은 필요에 따라 필드의 논리적 그룹을 만드는 데 사용됩니다.
  • 예외 : enum 상수 사이의 빈 줄은 섹션 4.8.1 에서 다룹니다.
  • 이 문서의 다른 섹션에서 요구하는대로 . 예를 들어 코드를 논리적 하위 섹션으로 구헝하는 명령문 사이와 같이 가독성을 향상시키는 모든 곳에 빈 줄 하나가 나타날 수 있습니다.
  • 첫 번째 멤버나 이니셜라이저 앞이나 클래스의 마지막 멤버나 이니셜라이저 뒤의 빈 줄은 권장되거나 권장되지 않습니다. (????)

→ 가장 지적을 많이 받았던 부분입니다. 필드, 생성자, 메소드, 중첩 클래스, 정적 초기화, 인스턴스 초기화 부분에서 개행을 진행하는 것을 권장하나 이전 코드 작성에서는 클래스 시작 최초 1회에서만 개행을 진행 하고 별도로 구분하지 않았습니다. 리뷰 반영하여 구분하니 가독성이 높아졌던 것 같습니다. 동일한 컨벤션을 유지하도록 항상 명심해야겠습니다!

4.6.2 수평 공백 (띄어쓰기)

언어 또는 기타 스타일 규칙에서 요구하는 경우 외에 리터럴, 주석 및 Javadoc 을 제외하고 단일 ASCII 공간은 다음 위치에만 나타납니다.

  • 예약어를 나누는 경우 : 해당 줄에서 뒤에 오는 여는 괄호 ( 에서 if , for 또는 catch 이후 나오는 여는 괄호 에서 사용
  • 예약어를 나누는 경우 : else 또는 catch 이후 나오는 닫는 중괄호} 에서 분리
  • 여는 중괄호 { 앞, 두 가지 예외:
    • @SomeAnnotation({a, b}) (공백이 사용되지 않음)
    • String[][] x = {{"foo"}};( {{아래 8 번 항목 사이에 공백이 필요하지 않음 )
  • 이항 또는 삼항 연산자의 양쪽 : 이는 다음 “Operator-like” 기호에도 적용됩니다.
    • 인접한 Type 바인딩의 앰퍼샌드 : <T extends Foo & Bar>
    • 여러 예외를 처리하는 catch 블록에 대한 파이프 : catch (FooException | BarException e)
    • for("foreach") 문 에서 콜론 :
    • 람다 식의 화살표 : (String str) -> str.length()
    • 하지만 아래 같은 경우에서는 사용하지 않습니다.
      • 메소드 참조 의 두 콜론 ::은 다음과 같이 작성됩니다 : Object::toString
      • 다음.과 같이 쓰여진 점 구분 기호 : object.toString()
  • 캐스트 뒤 ,:;또는 닫는 괄호 )
  • //줄 끝 주석을 시작하는 이중 슬래시의 양쪽 . 여기에서는 여러 개의 공백이 허용되지만 필수는 아닙니다.
  • 선언의 type과 변수 사이 : List list
  • (선택 사항) 배열 선언문 괄호 안에 공백 :new int[] {5, 6} 또는 new int[] { 5, 6 }
  • type annotation과 [] 또는 ...

4.6.3 수평 정렬: 필요 없음

💡 수평 정렬은 특정 토큰이 이전 줄의 다른 특정 토큰 바로 아래에 표시되도록 코드에 다양한 수의 추가 공백을 추가하는 방법입니다.

 

→ 쉽게 말해서, 열을 맞추는 행위입니다.

이 관행은 허용되지만, Google style에서 요구하는 것은 아닙니다.

이미 사용 된 장소에서 수평 정렬을 유지할 필요조차 없습니다.

다음은 정렬없이 수동 정렬을 사용하는 예 입니다.

private int x; // OK
private Color color; // OK

private int   x;      // OK, but future edits
private Color color;  // 맞춰지지 않은 상태로 둘 수 있음

→ 이 부분은 당장은 보기 좋을 수 있으나, 향후에 문제가 될 수 있을 것 같습니다. 아무래도 자동 정렬을 사용하는 것이 좋을 것 같습니다.

4.7 그룹화 괄호: 권장

선택적 그룹화 괄호는 작성자와 검토자가 코드가 없으면 코드가 잘못 해석 될 가능성이 없으며 코드를 읽기 쉽게 만들지 않았다는 데 동의하는 경우에만 생략됩니다. 모든 독자가 자바 연산자 우선 순위 테이블이 있다고 가정하는 것은 옳지 않습니다. 우선순위가 명확해도 괄호로 감싸는게 좋습니다.

4.8 특정 구조

4.8.1 Enum 클래스

enum 상수 뒤에 오는 각 쉼표 뒤에 줄 바꿈은 선택 사항입니다.

추가 빈 줄 도 허용됩니다.

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

  NO,
  MAYBE
}

method와 주석이 없는 enum class 는 배열 초기화와 같은 포멧으로 정의될 수 있습니다.

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

enum 클래스는 classes 므로 클래스 형식에 대한 다른 모든 규칙이 적용됩니다. ( 이전 규칙들을 적용합니다. )

4.8.2 변수 선언

4.8.2.1 선언 당 하나의 변수

모든 변수 선언은 하나의 변수만 선언합니다.

int a, b; 는 사용되지 않습니다.

예외 : for 루프 헤더

4.8.2.2 필요할 때 선언

지역 변수는 포함 블록이나 블록과 유사한 구조의 시작 부분에서 습관적으로 선언되지 않습니다. 대신 지역 변수는 범위를 최소화하기 위해 처음 사용되는 지점에 가깝게 선언됩니다. 지역 변수 선언에는 일반적으로 이니셜라이저가 있거나 선언 직후에 초기화됩니다.

4.8.3 Array

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 스타일 배열 선언 없음

대괄호는 변수가 아닌 type의 일부를 형성합니다:

String[] args → O, String args[] → X

4.8.4 Switch 문

용어 참고

스위치 블록의 중괄호 안에는 하나 이상의 구문 그룹이 있습니다.

각 구문 그룹은 하나 이상의 switch 라벨과 하나 이상의 명령문으로 구성됩니다.

4.8.4.1 들여쓰기

다른 블록과 마찬가지로 스위치 블록의 내용은 +2 로 들여쓰기 됩니다.

스위치 레이블 뒤에는 줄 바꿈이 있고 들여쓰기 수준은 마치 블록이 열려있는 것 처럼 +2가 증가합니다. 다음 스위치 레이블은 마치 블록이 닫힌 것처럼 이전 들여쓰기 수준으로 돌아갑니다.

4.8.4.2 Fall-through : 주석

Switch 블록 내에서 각 문 그룹 중 하나( break, contirnue, return 또는 발생한 예외) 가 돌연 종료되서나, 다음 구문으로 넘어가게 적을 수 있습니다.

여기에는 주석을 달 수 있습니다. 이 특수 주석은 스위치 블록 마지막 명령문 그룹에는 필요하지 않습니다.

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

4.8.4.3 default 케이스 존재

각 switch 문에는 default 코드가 없는 경우에도 default 문 그룹이 포함됩니다.

예외 : enum 유형에 대한 switch문은 해당 유형의 가능한 모든 경우를 포함하는 명시적 케이스를 처리한 경우 명령문 그룹을 생략할 수 있습니다.

이를 통해 IDE 또는 기타 정적 분석 도구는 누락 된 사례가 있는 경우 경고를 발행할 수 있습니다.

4.8.5 Annotations

클래스, 메서드 또는 생성자에 적용되는 Annotations은 documentation block 바로 뒤에 나타나며 각 어노테이션 자체 줄에 나열됩니다. 이러한 줄 바꿈은 줄 바꿈을 구성하지 않으므로, 들여쓰기 수준이 증가하지 않습니다.

4.8.6 주석

줄 바꿈 앞에는 임의의 공백과 구현 주석이 올 수 있습니다.

이러한 주석은 행을 공백이 아닌 것으로 렌더링합니다.

4.8.6.1 블록 주석 스타일

블록 주석은 주변 코드와 동일한 수준에서 들여 쓰기됩니다. /* / 스타일이나 //… 스타일이 있을 수 있습니다. 여러 줄 /* … */ 주석의 경우 후속 줄은 이전 줄에 * 정렬 된 것으로 시작해야 합니다.

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

4.8.7 접근 제한자

클래스 및 멤버 접근제한자는 있는 경우 Java 언어 사양에서 권장하는 순서로 나타납니다.

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

4.8.8 숫자 리터럴

lon 값을 갖는 정수 리터럴 L은 소문자가 아닌 대문자 접미사를 사용합니다.

Example ) 300000L → O , 300000l → X

5. Naming

5.1 모든 식별자에 공통적인 규칙

식별자는 ASCII 문자와 숫자만 사용하며 _ 를 사용하기도 하여서 표시합니다.

따라서 각 유효한 식별자 이름은 \w+ 정규식과 일치합니다.

Google 스타일에서는 특수 접두사 또는 접미사가 사용되지 않습니다.

예를 들어, 이 이름들은 구글 스타일이 아닙니다: name_, mName, s_name 와 kName.

5.2 식별자 유형별 규칙

5.2.1 Package 이름

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

예를 들어, com.example.deppspacenot → O, com.example.deepSpace or com.example.deep_space → X

5.2.2 클래스 이름

클래스 이름은 UpperCamelCase로 작성됩니다.

클래스 이름은 일반적으로 명사 또는 명사구입니다.

예를 들어, Character 또는 ImmutableList 입니다.

Interface 이름은 명사 구 또는 명사 일 수도 있지만 때로는 대신 형용사 또는 형용사 구 (ex, Readable) 일 수도 있습니다.

5.2.3 메서드 이름

메서드 이름은 lowerCamelCase로 작성됩니다.

메서드 이름은 일반적으로 동사 또는 동사 구 입니다.

example⇒ sendMessage , stop

5.2.4 상수 이름

상수 이름은 CONSTANT_CASE를 사용합니다: 모두 대문자 + 밑줄_ 로 각 단어를 구분합니다.

5.2.5 상수가 아닌 필드 이름

상수가 아닌 필드 이름은 lowerCamelCase로 작성됩니다.

이러한 이름은 일반적으로 명사 또는 명사 구입니다.

예로 computedValues , index 가 있습니다.

5.2.6 파라미터 이름

파라미터 이름은 lowerCamelCase로 작성됩니다.

공용 메소드에서 한 문자 파라미터 이름은 피하는게 좋습니다.

5.2.7 지역 변수 이름

지역 변수 이름은 lowerCamelCase로 작성됩니다.

final, 불변인 경우에도 지역 변수는 상수로 간주되지 않으며 상수로 스타일을 지정해서는 안되겠습니다.

 

 

 


이렇게 주요하게 사용되고 필요하다고 생각되는 부분에 대한 Google Java Style Guide를 살펴보았습니다. 한순간에 완성되지는 않겠지만, 두고두고 참고하며 깔끔한 코드를 작성하도록 노력해야 하겠습니다.

참고:

https://newwisdom.tistory.com/96

https://google.github.io/styleguide/javaguide.html