구글 자바 스타일 가이드
#우테코/코딩스타일
구글 자바 스타일 가이드 문서를 읽으면서 평소 나의 스타일과 다른 부분이나, 특히 신경써야 할 중요한 부분을 정리해보겠다.
Google Java Style Guide와
https://github.com/JunHoPark93/google-java-styleguide 를 참고하였다.
1.1 용어 노트
가이드 문서를 읽기 전에 헷갈리지 말라고 아래 말을 적어둔 것 같다.
- class라는 용어는 "일반적인" 클래스, enum 클래스, 인터페이스 혹은 애노테이션 타입을 포괄하여 쓰인다.
- member(혹은 class)라는 용어는 중첩 클래스, 필드, 메소드 혹은 생성자 즉, 초기화들과 주석들을 제외한 클래스의 모든 최상위 내용들을 포괄하여 쓰인다.
- comment라는 용어는 항상 구현 주석을 나타낸다. 우리는 "documentation comments"라는 용어 대신에 "Javadoc"이라는 용어를 쓴다.
2. 소스파일 기본 사항
2.3 특수 문자
2.3.1 공백 문자
- ASCII 수평 공백 문자 (0x20) 외의 모든 공백 문자는 소스 파일 어디에도 사용되지 않습니다.
- 이는 다음을 의미합니다:
- 문자열과 문자 리터럴 내의 다른 모든 공백 문자는 이스케이프 처리되어야 합니다.
- 들여쓰기에 탭 문자는 사용하지 않습니다.
아마 IDE 설정값대로 들여쓰기가 되는 것도 있고, 탭 대신 스페이스를 사용하는 사람이 더 많아서 그럴거라고 생각한다.
우테코에서는 블럭 들여쓰기가 +4 스페이스이다. 구글 자바 스타일 가이드는 +2 스페이스이다.
2.3.2 특수 이스케이프 시퀀스 (Special Escape Sequences)
- 특수 이스케이프 시퀀스(\b, \t, \n, \f, \r, ", ', \)는 해당하는 옥탈(\012)이나 유니코드(\u000a) 이스케이프 시퀀스 대신 사용됩니다.
아래처럼 옥탈이나 유니코드 이스케이프 시퀀스를 사용하지 말라는 것.
// 올바른 예시
String newline = "Line1\nLine2";
// 잘못된 예시
String newline = "Line1\012Line2";
// 잘못된 예시
String newline = "Line1\u000ALine2";
2.3.3 비-ASCII 문자 (Non-ASCII Characters)
- 비-ASCII 문자는 실제 유니코드 문자(예: ∞) 또는 해당하는 유니코드 이스케이프(\u221e)를 사용하여 표현할 수 있습니다.
- 선택은 코드를 더 읽기 쉽고 이해하기 쉽게 만드는 데만 의존하며, 문자열 리터럴과 주석 외부에서 유니코드 이스케이프의 사용은 강력히 권장되지 않습니다.
3. 소스 파일 구조
소스 파일은 다음과 같은 순서로 구성됩됩니다
- 라이선스 또는 저작권 정보 (있는 경우)
- 패키지 선언
- 임포트 선언
- 정확히 하나의 최상위 클래스
- 각 섹션 사이에 정확히 하나의 빈 줄을 삽입
3.1 라이선스 또는 저작권 정보 (License or Copyright)
- 파일에 라이선스 또는 저작권 정보가 포함되어야 할 경우, 이는 소스 파일의 최상단에 위치해야 합니다.
예시:
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*/
package com.example.myproject;
3.2 패키지 선언 (Package Statement)
- 패키지 선언은 한 줄로 작성하며, 줄 바꿈을 하지 않습니다.
- 패키지 선언은 소스 파일의 두 번째 줄에 위치하며, 열(column) 제한(섹션 4.4, 열 제한: 100)은 적용되지 않습니다.
예시:
package com.example.myproject.utilities;
3.3 임포트 선언 (Import Statements)
3.3.1 와일드카드 임포트 금지 (No Wildcard Imports)
와일드카드 임포트(*
)는 사용하지 않습니다. 가능한 한 구체적인 클래스를 임포트합니다. static import에도 동일하게 적용된다~
예시:
// 권장
import java.util.List;
import java.util.Map;
// 비권장
import java.util.*;
// 권장: 명시적으로 필요한 static 멤버만 임포트
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
// 비권장: static 와일드카드 임포트 사용
import static java.lang.Math.*;
3.3.2 줄 바꿈 금지 (No Line-Wrapping)
- 임포트 선언은 줄 바꿈 없이 한 줄로 작성합니다.
- 열 제한(섹션 4.4, 열 제한: 100)은 임포트 선언에 적용되지 않습니다.
예시:
import java.util.List;
import java.util.Map;
3.3.3 정렬 및 간격 (Ordering and Spacing)
- 임포트는 정적 임포트와 비정적 임포트를 별도의 블록으로 구분하여 정렬합니다.
- 정적 임포트가 있는 경우, 정적 임포트 블록과 비정적 임포트 블록 사이에 하나의 빈 줄을 삽입합니다.
- 각 블록 내에서는 임포트된 이름을 ASCII 순으로 정렬합니다.
예시:
import java.util.List;
import java.util.Map;
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import com.google.common.collect.ImmutableList;
import com.example.myproject.utils.Helper;
3.3.4 클래스에 대한 정적 임포트 금지 (No Static Import for Classes)
정적 임포트를 사용하여 정적 중첩 클래스를 임포트하지 않습니다. 대신 일반 임포트를 사용합니다.
예시:
// 잘못된 예시: 정적 임포트로 중첩 클래스 임포트
import static com.example.OuterClass.InnerClass;
// 권장 예시: 일반 임포트 사용
import com.example.OuterClass.InnerClass;
3.4 클래스 선언 (Class Declaration)
클래스 선언 시 반드시 다음 규칙을 준수해야 합니다.
3.4.1 정확히 하나의 최상위 클래스 선언 (Exactly One Top-Level Class Declaration)
- 각 소스 파일에는 정확히 하나의 최상위 클래스가 포함되어야 합니다.
- 최상위 클래스는 파일 이름과 동일해야 합니다.
예시:
// 파일 이름: DataProcessor.java
public class DataProcessor {
// 클래스 내용
}
3.4.2 클래스 구성 요소의 순서 (Ordering of Class Contents)
클래스 내의 멤버와 초기화 블록의 순서는 논리적이어야 하며, 일관된 규칙을 따라야 합니다. 하지만 정해진 하나의 올바른 순서는 없으며, 클래스의 목적에 따라 적절히 구성해야 합니다.
중요한 점:
- 각 클래스는 논리적인 순서를 사용해야 하며, 유지보수자가 그 순서를 설명할 수 있어야 합니다.
- 예를 들어, 새로운 메서드를 단순히 클래스의 끝에 추가하는 것은 "추가된 날짜순"의 비논리적인 순서를 초래할 수 있습니다.
3.4.2.1 오버로드: 절대 분리하지 않기 (Overloads: Never Split)
- 동일한 이름을 가진 메서드들은 하나의 연속된 그룹으로 배치하며, 그 사이에 다른 멤버가 들어가지 않도록 합니다.
- 동일한 이름의 생성자도 마찬가지로 분리되지 않아야 합니다.
- 이는 메서드의 접근 제어자(
static
,private
등)와 상관없이 적용됩니다.
예시:
public class Calculator {
// 덧셈 메서드 오버로드
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
// 곱셈 메서드
public int multiply(int a, int b) {
return a * b;
}
// 생성자 오버로드
public Calculator() {
// 기본 생성자
}
public Calculator(String name) {
// 이름을 받는 생성자
}
}
위 예시에서 add
메서드의 오버로드는 서로 인접해 있으며, 그 사이에 다른 메서드가 들어가지 않았습니다. 생성자 오버로드도 마찬가지로 연속적으로 배치되어 있습니다.
4. 포매팅
4.1 괄호
4.1.1 괄호는 선택사항에서도 쓰인다.
if
, else
, for
, do
, while
문에서는 블록이 비어 있거나 단일 문장만 포함할 경우에도 중괄호를 사용해야 합니다. 이는 코드의 일관성을 유지하고, 추후에 블록을 확장할 때 실수를 방지하기 위함입니다.
PS할 때 처럼 line 수를 줄이려고 괄호를 최대한 안 적는 행동은 하지 않아야 한다.
예시:
// 올바른 예시: 중괄호 사용
if (condition) {
doSomething();
} else {
doSomethingElse();
}
// 올바른 예시: 단일 문장에도 중괄호 사용
for (int i = 0; i < n; i++) {
process(i);
}
반면, 람다 표현식과 같은 다른 선택적 중괄호는 여전히 선택적으로 사용할 수 있습니다.
예시:
// 선택적으로 중괄호 사용 가능
list.forEach(item -> System.out.println(item));
list.forEach(item -> {
if (item.isValid()) {
process(item);
}
});
4.1.2 비어 있지 않은 블록: K & R 스타일
비어 있지 않은 블록이나 블록-유사 구조에서는 Kernighan and Ritchie 스타일("Egyptian brackets")을 따릅니다.
- 열림 중괄호
{
앞에 줄 바꿈을 하지 않습니다. - 열림 중괄호 다음에 줄 바꿈을 합니다.
- 닫힘 중괄호
}
앞에 줄 바꿈을 합니다. - 닫힘 중괄호 다음에 줄 바꿈을 할지 여부는 그 중괄호가 문장을 종료하거나 메서드, 생성자, 클래스의 본문을 종료하는지에 따라 결정합니다.
예시:
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 스타일을 따르거나, 열림 중괄호와 닫힘 중괄호를 바로 이어서 작성할 수 있습니다 ({}
). 단, 다중 블록 문(statement) 내에서는 간결한 빈 블록을 사용할 수 없습니다.
예시:
// 허용됨
void doNothing() {}
// 허용됨
void doNothingElse() {
}
// 허용되지 않음: 다중 블록 문 내에서 간결한 빈 블록 사용
try {
doSomething();
} catch (Exception e) {}
4.2 블록 들여쓰기: +2 스페이스
새로운 블록이나 블록-유사 구조가 열릴 때마다 들여쓰기를 2스페이스씩 증가시킵니다. 블록이 닫힐 때는 이전 들여쓰기 수준으로 돌아갑니다. 이는 코드와 주석 모두에 적용됩니다.
우테코에서는 + 4 스페이스이다.
예시:
public void exampleMethod() {
if (condition) {
doSomething();
}
}
4.3 한 줄에 한 문장
각 문장은 반드시 줄 바꿈으로 구분되어야 합니다. 이는 코드의 가독성을 높이고, 디버깅과 유지보수를 용이하게 합니다.
예시:
int a = 5;
int b = 10;
int sum = a + b;
System.out.println(sum);
4.4 열 제한: 100
자바 코드는 한 줄에 최대 100자까지 작성해야 합니다. 이 규칙은 코드의 가독성을 높이고, 다양한 편집기와 도구에서의 호환성을 보장합니다.
- 예외 사항:
- 줄 제한을 지키기 어려운 경우 (예: Javadoc 내의 긴 URL, JSNI 메서드 참조 등)
- 패키지 선언 및 임포트 선언
- 쉘에 복사-붙여넣기 가능한 주석 내의 긴 명령어
- 매우 긴 식별자 (드물게 필요할 때)
예시:
String longString = "이것은 매우 긴 문자열로, 코드의 가독성을 유지하기 위해 "
+ "적절히 줄을 나누어 작성합니다.";
4.5 줄 바꿈 (Line-wrapping)
매 상황별 어떻게 줄 바꿈하는지에 대한 정확한 방법은 없다. 주로 같은 조각의 코드에서 줄 바꿈을 한다.
참고: 줄 바꿈의 전형적 의도는 행 제한을 넘지 않기 위해서다. 심지어는 줄 제한에 걸리지 않더라도 저자의 재량에 따라 줄 바꿈이 될 수 있다.
4.5.1 줄을 어디서 끊을지
- 높은 구문 수준에서 끊기: 예를 들어, 연산자 앞에서 끊습니다.
- 특정 기호 앞에서 끊기: 점(
.
), 메서드 참조(::
), 타입 경계에서의 앰퍼샌드(&
), 캐치 블록 내의 파이프(|
). - 할당 연산자 앞/뒤에서 끊기: 할당 연산자 앞에서 끊는 것이 일반적이지만, 뒤에서도 허용됩니다.
- 메서드 이름과 여는 괄호는 함께 유지: 메서드나 생성자 이름은 여는 괄호와 함께 유지합니다.
- 쉼표는 앞에 유지: 쉼표는 이전 토큰과 함께 유지합니다.
- 람다의 화살표(
->
) 근처에서는 줄을 끊지 않습니다. 단, 단일 표현식인 경우 화살표 뒤에서 줄을 끊을 수 있습니다.
예시:
MyLambda<String, Long, Object> lambda =
(String label, Long value, Object obj) -> {
...
};
Predicate<String> predicate = str ->
longExpressionInvolving(str);
4.5.2 계속 줄 들여쓰기: 최소 +4 스페이스
줄을 나눌 때, 첫 번째 줄 이후의 모든 줄은 최소 4스페이스 이상 들여쓰기를 해야 합니다. 여러 줄에 걸쳐 있을 경우, 들여쓰기를 더 추가할 수 있습니다.
예시:
String result = someObject
.methodOne()
.methodTwo()
.methodThree();
4.6 공백 (Whitespace)
공백 문자는 코드의 가독성을 높이는 데 사용됩니다. 구글 자바 스타일 가이드에서는 수직 및 수평 공백 사용에 대한 규칙을 명확히 하고 있습니다.
4.6.1 수직 공백 (Vertical Whitespace)
- 단일 빈 줄 사용: 클래스의 연속된 멤버나 초기화 블록 사이에 단일 빈 줄을 삽입합니다.
- 예: 필드, 생성자, 메서드, 중첩 클래스, 정적 초기화 블록, 인스턴스 초기화 블록 사이
- 예외 사항:
- 두 연속된 필드 사이에 빈 줄을 삽입하는 것은 선택 사항입니다.
- 열거형 상수 사이의 빈 줄은 4.8.1 섹션에서 다룹니다.
- 추가 빈 줄: 코드의 논리적 구획을 나누기 위해 사용될 수 있습니다.
예시:
public class Example {
private int count;
public Example() {
// 생성자 내용
}
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
4.6.2 수평 공백 (Horizontal Whitespace)
수평 공백은 코드 내에서 특정 위치에만 사용됩니다. 불필요한 공백은 피하고, 다음과 같은 경우에만 단일 스페이스를 사용합니다.
- 예약어와 여는 괄호 사이:
if (condition)
에서if
와(
사이에 스페이스를 넣는다. - 예약어와 닫는 괄호 사이:
else if (condition)
에서else
와if
사이에 스페이스를 넣는다. - 열림 중괄호 앞: 애노테이션, 배열 초기화 등 특정 예외를 제외하고 중괄호 여는 모든 경우에 사용
- @SomeAnnotation({a, b})
- String x = {{"foo"}};
- 이항 또는 삼항 연산자 양쪽:
a + b
,a ? b : c
등- 인접한 타입 바운딩의 앰퍼센드 연산자에서
- catch (FooException | BarException e) 예외처리의 파이프 에서
- for ("foreach") statement 향상된 for 문에서
- (String str) str.length() 람다의 화살표에서
- 하지만 두개의 :: 콜론에서는 띄우면 안된다. Object::toString
- 그리고 dot(.) 연산자에서도 띄우면 안된다. object.toString()
- 콤마, 콜론, 세미콜론, 캐스트의 닫는 괄호 후:
List<String> list
,catch (Exception e) {
- 문자열 내 주석과의 간격: // 더블 슬래시에서 양쪽에 사용. 여러 수의 공백이 허용되지만 필수사항은 아니다.
- 배열 선언문 사이에서 공백 선택
- new int {5, 6}andnew int { 5, 6 } 둘 다 가능
- 타입 애너테이션이나 대괄호에서 사용
예시:
int sum = a + b;
for (int i = 0; i < n; i++) {
// 루프 내용
}
if (condition) {
doSomething();
} else {
doSomethingElse();
}
List<String> list = new ArrayList<>();
4.6.3 수평 정렬: 필요 없음!!
수평 정렬은 특정 토큰을 이전 줄의 토큰 아래에 맞추기 위해 추가적인 공백을 사용하는 것을 의미합니다. 구글 스타일 가이드는 이를 필요하지 않다고 명시하고 있습니다. 수평 정렬을 시도하면 코드의 수정이 어려워지고, 버전 관리에서 문제를 일으킬 수 있기 때문에 권장하지 않습니다.
예시:
private int x; // 허용됨
private Color color; // 허용됨
private int x; // 허용됨, 하지만 비추천
private Color color; // 허용됨, 하지만 비추천
팁: 정렬을 유지하려고 추가적인 공백을 사용하는 것보다, 코드의 명확성과 가독성을 우선시해야 합니다.
4.7 그룹핑 괄호: 권장
선택적 그룹 괄호는 작성자와 검토자가 코드가 없으면 잘못 해석 될 가능성이 없으며 코드를 읽기 쉽게 만든다는 데 동의하지 않는 경우에만 생략된다. 모든 독자가 전체 Java 연산자 우선 순위 테이블을 가지고 있다고 가정하는 것은 합리적이지 않습니다. 즉, 우선순위 연산자가 명확하더라도 소괄호로 감싸는것을 추천한다는 말이다.
예시:
public void exampleMethod() {
if ((a > b) && (c < d)) {
// 조건이 명확히 그룹핑됨
}
}
팁: 모든 독자가 자바의 연산자 우선순위를 완벽히 기억하고 있다고 가정하지 않으므로, 그룹핑 괄호를 사용하는 것이 코드의 이해를 돕습니다.
4.8 특정 구조 (Specific Constructs)
아까 위에서 Enum에서 예외 규칙이 적용된다 한 부분을 살펴보자.
4.8.1 열거형 클래스 (Enum Classes)
열거형 클래스에서는 각 열거형 상수 뒤에 쉼표를 사용할 때 줄 바꿈이 선택적입니다. 또한, 메서드가 없는 간단한 열거형 클래스는 배열 초기화 형식으로 작성할 수 있습니다.
예시:
private enum Answer {
YES {
@Override public String toString() {
return "yes";
}
},
NO,
MAYBE
}
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
참고: Enum 클래스는 클래스이므로, 다른 클래스 포매팅 규칙이 모두 적용됩니다.
4.8.2 변수 선언 (Variable Declarations)
4.8.2.1 한 선언에 하나의 변수 (One Variable per Declaration)
모든 변수 선언은 한 번에 하나의 변수만 선언해야 합니다. int a, b;
와 같은 선언은 사용하지 않습니다. 단, for
루프의 헤더에서 여러 변수를 선언하는 것은 예외로 허용됩니다.
예시:
// 권장됨
int a;
int b;
// 비권장됨
int a, b;
// for 루프에서는 허용됨
for (int i = 0, j = 0; i < n; i++, j++) {
// 루프 내용
}
4.8.2.2 필요할 때 선언 (Declared When Needed)
지역 변수는 가능한 한 사용되는 지점 가까이에 선언해야 하며, 변수의 범위를 최소화해야 합니다. 이는 변수의 사용 범위를 줄여 코드의 명확성을 높이고, 실수를 방지합니다.
예시:
public void calculate() {
// 불필요하게 블록 시작 시점에 변수 선언 금지
// int result;
// 필요할 때 변수 선언
int result = performCalculation();
System.out.println(result);
}
4.8.3 배열 (Arrays)
4.8.3.1 배열 초기화: "블록-유사" 형식 가능 (Array Initializers: Can Be "Block-like")
배열 초기화는 블록-유사 형식으로 작성할 수 있습니다. 이는 배열의 각 요소를 새로운 줄에 작성하여 가독성을 높일 수 있습니다.
예시:
new int[] {
0, 1, 2, 3
}
new int[] {
0,
1,
2,
3
}
new int[] {
0, 1,
2, 3
}
new int[]
{0, 1, 2, 3}
4.8.3.2 C 스타일 배열 선언 금지 (No C-style Array Declarations)
배열의 대괄호는 타입의 일부로 간주하며, 변수 이름과는 분리해서 작성해야 합니다. C 스타일의 배열 선언 (String args[]
)은 사용하지 않고, 자바 스타일 (String[] args
)을 사용해야 합니다.
예시:
// 권장됨
String[] args;
// 비권장됨
String args[];
4.8.4 Switch 문 (Switch Statements)
Terminology Note: Switch 블록 내의 중괄호 안에는 하나 이상의 문장 그룹(statement groups)이 있습니다. 각 문장 그룹은 하나 이상의 switch 레이블(case 또는 default)과 하나 이상의 문장으로 구성됩니다.
4.8.4.1 들여쓰기 (Indentation)
Switch 블록의 내용은 다른 블록과 마찬가지로 +2 스페이스 들여쓰기를 적용합니다. 각 switch 레이블(case, default) 뒤에는 줄 바꿈이 있으며, 그 뒤에 블록처럼 들여쓰기가 적용됩니다.
예시:
switch (input) {
case 1:
doSomething();
break;
case 2:
doSomethingElse();
break;
default:
doDefault();
break;
}
4.8.4.2 Fall-through: 주석으로 표시 (Fall-through: Commented)
Switch 블록 내에서 특정 케이스가 다음 케이스로 "fall-through"될 경우, 이를 주석으로 명시해야 합니다. 이는 코드의 명확성을 높이고, 의도치 않은 fall-through를 방지하기 위함입니다.
예시:
switch (input) {
case 1:
case 2:
prepareOneOrTwo();
// fall through
case 3:
handleOneTwoOrThree();
break;
default:
handleLargeNumber(input);
}
주의: 마지막 문장 그룹 이후에는 주석이 필요하지 않습니다.
4.8.4.3 기본(default) 레이블 존재 (Presence of the Default Label)
모든 switch 문에는 반드시 기본(default) 레이블이 포함되어야 합니다. 단, 열거형 타입의 switch 문에서는 모든 가능한 값을 명시적으로 처리하는 경우 기본 레이블을 생략할 수 있습니다. 이는 IDE나 정적 분석 도구가 누락된 케이스를 경고할 수 있도록 하기 위함입니다.
예시:
switch (day) {
case MONDAY:
handleMonday();
break;
case FRIDAY:
handleFriday();
break;
default:
handleDefault();
break;
}
4.8.5 애노테이션 (Annotations)
애노테이션은 코드에 메타데이터를 추가하는 방법으로, 컴파일러나 런타임에서 이를 활용할 수 있습니다.
애노테이션은 documentation 블럭 바로 이후에 클래스나 함수 혹은 생성자에 적용된다. 그리고 각 애노테이션들은 그들 만의 줄을 가지고 있다. (즉 하나의 애노테이션은 한 줄에) 이러한 개행들은 줄 바꿈에 해당되지 않는다. (4.5 절 줄바꿈) 그래서 들여쓰기 레벨이 증가되지 않는다. 예를들어:
@Override
@Nullable
public String getNameIfPresent() { ... }
예외: 파라미터가 없는 단일 애노테이션은 한줄에 쓸 수 있다. 예를들어,
@Override public int hashCode() { ... }
필드에 적용되는 애노테이션들은 (파라미터가 있을 수 있음) 한 줄에 쓸 수 있다. 예를들어,
@Partial @Mock DataLoader loader;
파라미터나 지역변수 혹은 타입에 적용되는 애노테이션의 특정한 포맷팅 규칙은 없다.
4.8.6 주석 (Comments)
주석은 코드의 의도를 설명하고, 복잡한 로직을 이해하는 데 도움을 줍니다. 구글 자바 스타일 가이드에서는 구현 주석과 문서화 주석(Javadoc)에 대한 규칙을 별도로 다룹니다.
4.8.6.1 블록 주석 스타일 (Block Comment Style)
블록 주석은 주변 코드와 동일한 들여쓰기 수준에 맞추어 작성합니다. 멀티라인 /* ... */
주석은 각 줄이 별도의 *
로 시작하도록 작성해야 합니다.
예시:
/*
* 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
예시:
public static final int MAX_VALUE = 100;
private volatile boolean isActive;
4.8.8 숫자 리터럴 (Numeric Literals)
Long 타입 리터럴: long
값의 리터럴에는 대문자 L
을 사용하며, 소문자 l
은 사용하지 않습니다. 이는 소문자 l
이 숫자 1
과 혼동될 수 있기 때문입니다.
예시:
long maxConnections = 3000000000L; // 권장
long maxConnections = 3000000000l; // 비권장
5. 네이밍 규칙 (Naming Conventions)
5.1 모든 식별자에 공통된 규칙
- ASCII 문자 및 숫자만 사용: 식별자는 ASCII 문자(대소문자)와 숫자만을 사용하며, 소수의 경우에만 언더스코어(
_
)가 허용됩니다. 따라서 모든 유효한 식별자 이름은 정규 표현식\w+
과 일치합니다. - 특별한 접두사 또는 접미사 사용 금지: 구글 스타일에서는 특별한 접두사나 접미사를 사용하지 않습니다. 예를 들어,
name_
,mName
,s_name
,kName
과 같은 이름은 구글 스타일에 부합하지 않습니다.
잘못된 예시:
int name_;
private String mName;
public static final String s_name = "example";
private static final int kName = 100;
올바른 예시:
int name;
private String name;
public static final String NAME = "example";
private static final int NAME_CONSTANT = 100;
5.2 식별자 유형별 규칙
각 식별자 유형에 따라 네이밍 규칙이 다릅니다. 다음은 주요 식별자 유형별 규칙과 예시입니다.
5.2.1 패키지 이름
- 소문자 사용: 패키지 이름은 모두 소문자로 작성하며, 숫자를 사용할 수 있지만 언더스코어는 사용하지 않습니다.
- 단어 결합: 연속된 단어는 단순히 결합하여 사용합니다. 예를 들어,
com.example.deepspace
는 올바른 패키지 이름입니다.
잘못된 예시:
package com.example.deepSpace;
package com.example.deep_space;
올바른 예시:
package com.example.deepspace;
5.2.2 클래스 이름
- UpperCamelCase 사용: 클래스 이름은 대문자로 시작하며, 각 단어의 첫 글자를 대문자로 작성합니다.
- 명사 또는 명사구 사용: 클래스 이름은 일반적으로 명사 또는 명사구로 작성됩니다. 인터페이스 이름도 명사나 형용사구로 작성될 수 있습니다.
- 테스트 클래스: 테스트 클래스는 테스트하는 클래스의 이름에
Test
를 붙여 작성합니다. 예를 들어,HashImplTest
는HashImpl
클래스를 테스트하는 클래스입니다.
예시:
public class DataProcessor {
// 클래스 내용
}
public interface Readable {
void read();
}
public class HashImplTest {
// 테스트 클래스 내용
}
5.2.3 메서드 이름
- lowerCamelCase 사용: 메서드 이름은 소문자로 시작하며, 각 단어의 첫 글자를 대문자로 작성합니다.
- 동사 또는 동사구 사용: 메서드 이름은 일반적으로 동사나 동사구로 작성되어야 메서드의 동작을 명확히 표현합니다.
- JUnit 테스트 메서드: 테스트 메서드 이름에는 논리적 구성 요소를 구분하기 위해 언더스코어(
_
)를 사용할 수 있습니다.
예시:
public void calculateTotal() {
// 메서드 내용
}
public void sendMessage() {
// 메서드 내용
}
@Test
public void transferMoney_deductsFromSource() {
// 테스트 메서드 내용
}
5.2.4 상수 이름
- UPPER_SNAKE_CASE 사용: 상수 이름은 모두 대문자로 작성하며, 단어 사이에 언더스코어(
_
)를 사용하여 구분합니다. - 상수 정의: 상수는 static final 필드 인데 그것은 변경될 수 없고 그것들의 메소드는 부작용이 보여서는 안된다. 이것은 원시타입, 문자열 그리고 불변 타입, 불변타입의 불변 컬렉션을 포함한다. 만약 어떤 인스턴스의 상태가 바뀐다면 그것은 상수가아니다. 단지 객체의 상태를 변화시키지 않는 것이 목적은 아니다.
상수
// 상수
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(','); // Joiner가 불변이기 때문
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 사용: 비상수 필드(정적 또는 비정적)는 소문자로 시작하며, 각 단어의 첫 글자를 대문자로 작성합니다.
- 명사 또는 명사구 사용: 비상수 필드 이름은 일반적으로 명사나 명사구로 작성됩니다.
예시:
private int count;
private String computedValues;
private static final Logger LOGGER = Logger.getLogger(MyClass.class.getName());
5.2.6 매개변수 이름
- lowerCamelCase 사용: 매개변수 이름도 소문자로 시작하며, 각 단어의 첫 글자를 대문자로 작성합니다.
- 의미 있는 이름 사용: 매개변수 이름은 해당 매개변수가 담고 있는 데이터의 의미를 명확히 표현해야 합니다.
- public 메서드의 단일 문자 매개변수 피하기: public 메서드에서 한개의 문자를 가진 파라미터는 피해야 한다.
예시:
public void processData(final List<String> data) {
// 매개변수 이름은 'data'로 명확하게 표현
}
5.2.7 지역 변수 이름
- lowerCamelCase 사용: 지역 변수 이름도 소문자로 시작하며, 각 단어의 첫 글자를 대문자로 작성합니다.
- 비상수와 동일하게 취급: 지역 변수는 비상수로 간주되며, 상수 스타일(UPPER_SNAKE_CASE)을 사용하지 않습니다.
예시:
public void calculate() {
int result = performCalculation();
System.out.println(result);
}
5.2.8 타입 변수 이름
각 타입들은 두 스타일중 하나를 따른다.
- 하나의 대문자, 혹은 뒤에 하나의 숫자가 따라올 수 있다. (예를들어, E, T, X, T2)
- 클래스를 위해서 사용되는 (5.2.2절 클래스 이름 참조) 이름의 형식에 T 대문자가 따라오는 형식 (예를들어, RequestT, FooBarT)
예시:
public class Box<T> {
private T content;
public void setContent(T content) {
this.content = content;
}
public T getContent() {
return content;
}
}
public <E> void addElement(E element) {
// 메서드 내용
}
5.3 카멜 케이스 정의
카멜 케이스는 영어 문구를 카멜 케이스로 변환할 때 발생할 수 있는 다양한 경우를 처리하기 위한 규칙을 정의합니다. 이는 코드의 예측 가능성과 일관성을 높이기 위함입니다.
카멜 케이스 변환 규칙
- 문구를 순수 ASCII로 변환하고, 아포스트로피 제거:
- 예:
"Müller's algorithm"
→"Muellers algorithm"
- 예:
- 단어로 분할:
- 공백이나 남아 있는 구두점을 기준으로 단어를 분할합니다.
- 예:
"XML HTTP request"
→"XmlHttpRequest"
- 관용적인 카멜 케이스 분할 적용:
- 이미 일반적으로 카멜 케이스로 사용되는 단어는 그 구성 요소로 분할합니다.
- 예:
"AdWords"
→"adWords"
- 주의:
"iOS"
와 같이 관용적인 카멜 케이스에 속하지 않는 단어는 예외로 둡니다.
- 소문자화 후 첫 글자만 대문자로 변환:
- 모든 단어를 소문자로 변환한 후, UpperCamelCase 또는 lowerCamelCase로 변환합니다.
- 단일 식별자로 결합:
- 모든 단어를 하나의 식별자로 결합합니다.
예시
문구 | 올바른 변환 | 잘못된 변환 |
---|---|---|
"XML HTTP request" | XmlHttpRequest | XMLHTTPRequest |
"new customer ID" | newCustomerId | newCustomerID |
"inner stopwatch" | innerStopwatch | innerStopWatch |
"supports IPv6 on iOS?" | supportsIpv6OnIos | supportsIPv6OnIOS |
"YouTube importer" | YouTubeImporter | YoutubeImporter* |
"Turn on 2SV" | turnOn2sv | turnOn2Sv |
참고: 영어에서는 모호하게 하이픈이 있는 단어가 몇개 있따. 예를들어, "nonempty" 나 "non-empty"는 둘 다 맞다. 그래서 메소드 이름이 checkNonempty나 checkNonEmpty 둘다 맞다.
6. 프로그래밍 관행 (Programming Practices)
6.1 @Override: 항상 사용
- @Override 애너테이션 사용: 메서드가 슈퍼클래스의 메서드를 오버라이드하거나 인터페이스의 메서드를 구현할 때는 항상
@Override
애너테이션을 사용해야 합니다. - 예외: 부모 메서드가
@Deprecated
인 경우@Override
를 생략할 수 있습니다.
6.2 잡힌 예외: 무시하지 않기
- 예외 처리: 잡힌 예외를 무시하는 것은 매우 드물게 적절합니다. 일반적으로 예외를 처리하거나 로깅하거나, 불가능하다고 판단될 경우
AssertionError
로 재던지는 것이 바람직합니다. - 적절한 예외 처리: 예외를 무시해야 할 경우, 그 이유를 주석으로 명확히 설명해야 합니다.
- 테스트에서의 예외 무시: 테스트 코드에서는 특정 예외를 예상하고 이를 무시할 수 있습니다. 이 경우 별도의 주석이 필요하지 않습니다.
예시:
적절한 예외 처리:
try {
int i = Integer.parseInt(response);
return handleNumericResponse(i);
} catch (NumberFormatException ok) {
// 숫자가 아니면 그냥 텍스트 응답을 처리
}
return handleTextResponse(response);
테스트에서의 예외 무시:
try {
emptyStack.pop();
fail();
} catch (NoSuchElementException expected) {
// 예외가 예상되므로 아무런 조치도 필요 없음
}
6.3 정적 멤버: 클래스 이름으로 한정
- 정적 멤버 접근 시 클래스 이름 사용: 정적 멤버(필드, 메서드)를 참조할 때는 해당 클래스의 이름을 사용하여 한정해야 합니다. 이는 코드의 명확성을 높이고, 네임스페이스 충돌을 방지하기 위함입니다.
- 잘못된 접근 방식: 인스턴스나 표현식을 사용하여 정적 멤버에 접근하는 것은 금지됩니다.
예시:
올바른 접근:
Foo.aStaticMethod(); // 클래스 이름을 사용하여 접근
잘못된 접근:
Foo aFoo = new Foo();
aFoo.aStaticMethod(); // 잘못됨. 인스턴스로 접근
somethingThatYieldsAFoo().aStaticMethod(); // 매우 잘못됨. 표현식으로 접근
6.4 파이널라이저: 사용하지 않기
- 파이널라이저(Finalizers) 사용 금지:
Object.finalize
메서드를 오버라이드하지 말아야 합니다. 파이널라이제이션 지원은 제거될 예정이므로, 이를 사용하지 않는 것이 좋습니다.
7. Javadoc
7.1 포매팅 (Formatting)
7.1.1 일반 형식 (General Form)
Javadoc 블록의 기본 형식은 다음과 같습니다:
/**
* Multiple lines of Javadoc text are written here,
* wrapped normally...
*/
public int method(String p1) { ... }
또는 단일 줄로 작성할 수도 있습니다:
/** An especially short bit of Javadoc. */
기본 형식의 규칙:
- 멀티라인 Javadoc: 여러 줄로 작성되며, 각 줄은
*
으로 시작합니다. - 단일라인 Javadoc: 전체 Javadoc 블록(주석 마커 포함)이 한 줄에 모두 들어갈 때 사용합니다. 단, 이 경우 블록 태그(
@param
등)가 포함되지 않아야 합니다.
예시:
/**
* Calculates the sum of two integers.
*
* @param a the first integer
* @param b the second integer
* @return the sum of a and b
*/
public int add(int a, int b) {
return a + b;
}
/** Returns the current timestamp. */
public long getTimestamp() {
return System.currentTimeMillis();
}
7.1.2 문단 (Paragraphs)
규칙:
- 문단 간 공백: 문단 사이에는 빈 줄(즉,
*
만 있는 줄)이 하나 있어야 합니다. - 블록 태그 전 공백: 블록 태그가 존재할 경우, 블록 태그 전에 빈 줄을 삽입합니다.
- 문단 시작: 첫 번째 문단을 제외한 모든 문단의 시작 부분에
<p>
태그를 사용하며,<p>
태그 뒤에는 공백이 없어야 합니다. - 블록 레벨 HTML 태그:
<ul>
,<table>
등과 같은 블록 레벨 HTML 태그는<p>
태그 없이 작성됩니다.
예시:
/**
* Calculates the sum of two integers.
*
* This method takes two integers as input and returns their sum.
* It handles both positive and negative integers.
*
* @param a the first integer
* @param b the second integer
* @return the sum of a and b
*/
public int add(int a, int b) {
return a + b;
}
7.1.3 블록 태그 (Block Tags)
규칙:
- 표준 블록 태그 순서:
@param
,@return
,@throws
,@deprecated
순으로 작성합니다. - 빈 설명 금지: 블록 태그는 항상 설명을 포함해야 하며, 빈 설명은 허용되지 않습니다.
- 줄 바꿈 시 들여쓰기: 블록 태그가 한 줄에 모두 들어가지 않을 경우,
@
기호의 위치에서 최소 4스페이스 이상 들여쓰기 합니다.
예시:
/**
* Sends a message to the specified recipient.
*
* @param recipient the recipient of the message
* @param subject the subject of the message
* @param body the body of the message
* @throws MessagingException if the message could not be sent
*/
public void sendMessage(String recipient, String subject, String body) throws MessagingException {
// 메서드 내용
}
/**
* Performs a calculation and returns the result.
* This method handles various edge cases and ensures accuracy.
*
* @return the result of the calculation
*/
public double calculate() {
// 메서드 내용
}
7.2 요약 조각 (The Summary Fragment)
각 Javadoc 블록은 간략한 요약 조각으로 시작해야 합니다. 이 요약 조각은 문서의 인덱스나 검색 결과 등에 표시되므로 매우 중요합니다.
규칙:
- 요약 조각의 형태: 요약 조각은 조각(fragment) 형태로, 명사구 또는 동사구로 작성합니다. 완전한 문장(예: "This method returns...")이나 명령문(예: "Save the record.")은 피합니다.
- 문장처럼 대문자화 및 문장부호 사용: 요약 조각은 문장의 첫 글자를 대문자로 시작하고, 마침표 등 문장부호를 사용할 수 있습니다.
- 요약 조각의 예외: 블록 태그(
@return
등)가 포함되지 않은 경우에만 단일 줄로 작성할 수 있습니다.
팁: 주로 하는 실수: /** @return the customer ID / 이것은 잘못 되었고 /* Returns the customer ID. */로 바뀌어야 한다.
/**
* Returns the customer ID.
*
* @return the customer ID
*/
public String getCustomerId() {
return customerId;
}
잘못된 예시:
/**
* @return the customer ID
*/
public String getCustomerId() {
return customerId;
}
/**
* Save the record.
*
* @param record the record to save
*/
public void saveRecord(Record record) {
// 메서드 내용
}
팁: 단순히 @return
태그만 있는 Javadoc 블록은 피하고, 요약 조각을 포함시켜야 합니다.
7.3 Javadoc 사용 위치 (Where Javadoc is Used)
Javadoc은 코드의 이해를 돕기 위해 필요한 곳에만 사용해야 합니다. 과도한 Javadoc 사용은 오히려 가독성을 저해할 수 있으므로, 적절한 위치에만 사용하도록 합니다.
7.3.1 예외: 자기 설명적 멤버 (Exception: Self-explanatory Members)
규칙:
- 간단하고 명확한 멤버:
getFoo()
,setFoo()
,isFoo()
와 같이 이름만으로도 기능이 명확한 멤버는 Javadoc을 생략할 수 있습니다. 단, 이 경우 Javadoc이 단순히 "Returns the foo."와 같은 내용만 포함하지 않아야 합니다. - 주의: 예외 규칙을 이용하여 유용한 정보를 생략해서는 안 됩니다. 예를 들어,
getCanonicalName()
과 같은 메서드는 "Returns the canonical name."이라는 Javadoc 대신, 메서드가 무엇을 하는지 명확하게 설명하는 Javadoc을 작성해야 합니다.
올바른 예시:
/**
* Retrieves the canonical name of this object.
*
* @return the canonical name
*/
public String getCanonicalName() {
return canonicalName;
}
잘못된 예시:
/**
* @return the foo
*/
public Foo getFoo() {
return foo;
}
7.3.2 예외: 오버라이드 (Exception: Overrides)
규칙:
- 오버라이드된 메서드: 슈퍼클래스나 인터페이스의 메서드를 오버라이드하거나 구현할 때는 반드시 Javadoc을 작성할 필요는 없습니다. 단, 추가적인 설명이 필요할 경우에는 Javadoc을 작성할 수 있습니다.
- 주의: 오버라이드된 메서드의 Javadoc이 슈퍼클래스의 Javadoc을 단순히 반복하지 않아야 합니다.
예시:
public class MyList extends AbstractList<String> {
@Override
public String get(int index) {
// 메서드 내용
}
@Override
public int size() {
return internalList.size();
}
}
7.3.4 비필수 Javadoc (Non-required Javadoc)
규칙:
- 필수는 아니지만 권장됨: 클래스나 멤버의 전체 목적이나 동작을 정의하는 구현 주석이 필요한 경우, 해당 주석을 Javadoc 블록으로 작성해야 합니다.
- 포매팅 규칙 예외: 비필수 Javadoc은 반드시 포매팅 규칙(7.1.1, 7.1.2, 7.1.3, 7.2)을 따를 필요는 없지만, 가능한 한 권장되는 규칙을 따르는 것이 좋습니다.
예시:
/**
* Manages user sessions and handles authentication.
*/
public class SessionManager {
// 클래스 내용
}
/**
* Processes user input and updates the UI accordingly.
*/
public void handleUserInput(String input) {
// 메서드 내용
}
'우아한테크코스 > 프리코스' 카테고리의 다른 글
[우테코/프리코스] Week2. 2주차 프리코스 회고 (0) | 2024.11.08 |
---|---|
[우테코/프리코스] Week1. 1주차 프리코스 회고 (0) | 2024.11.05 |
[우테코/프리코스] Week0. 프리코스 시작 전 살펴보기 (0) | 2024.10.16 |