개발자라면 협업 시, 사용하는 언어의 코드 컨벤션을 지켜야 합니다.
이전까지는 느낌상 이렇게 하면 되겠다는 생각으로 개발을 했었는데,
굉장히 위험한 생각인 것 같아 정리하게 되었습니다.
이 포스트는 Google Java Style Guide 의 내용을 토대로 썼습니다.
Java 소스 파일은 아래의 규칙을 준수하는 경우에만 Google 스타일로 설명이 됩니다. Google에서는 어떤 코딩 스타일을 따르고 있는지 알아봅니다.
달리 명시되지 않는 한,
1. 클래스라는 용어는 "일반"클래스, 열거 형 클래스, 인터페이스 또는 주석 유형(@interface)을 의미하기 위해 포괄적으로 사용됩니다.
2. 클래스의 멤버라는 용어는 중첩 된 클래스, 필드, 메서드 또는 생성자를 의미하기 위해 포괄적으로 사용됩니다.
3. 주석이라는 용어는 항상 구현 주석을 의미합니다. "Documentation comments"라는 이름을 사용하지 않고 "Javadoc"이라는 일반적인 용어를 사용합니다.
- 클래스 이름과 동일하게 대소문자를 구별해서 작성. 확장자는 java.
-- ex) Car class가 구현된 소스파일 이름: Car.java
- 인코딩은 UTF-8로 통일.
- 스페이스키만 허용.
- 특수문자를 사용할 때는 (\b, \t, \n, \f, \r, \", \' and \)을 사용.
- special escape sequence를 참고
- 코드의 가독성을 높일 수 있다면 유니코드를 사용해도 됨.
소스코드는 위 형태로 선언되야함. 각 섹션 사이에는 공백 라인이 하나 들어감.
- 라이센스 또는 저작권 정보가 파일에 속하면 기입.
- 아무리 길어도 줄바꿈을 하지 않음.
- 와일드 카드(e.g. *)를 쓰지 않음, Package 문과 동일하게 줄바꿈 하지 않음.
- 그룹핑을 해서 순서에 맞춰 작성. 다른 그룹간에는 공백라인을 한 줄 추가.
- 그룹 이름 및 순서:
-- static import
-- non - static imports
- 최상위 클래스만 선언
- 클래스 멤버(1.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() {
}
- 스페이스키 2개로 정의
- 각 문장 뒤에는 줄 바꿈을 함
- 한 라인의 문자는 100개만 씀. 그 보다 길 경우 다음 라인으로 내림.
- 코드의 길이가 페이지 넓이를 넘어갈 때, 하나의 문장을 두 문장 이상으로 나눠서 표현하는 것.
- 절대적인 법칙은 없고 상황에 따라서 적절하게 사용하면 됨
- 줄 바꿈의 일반적인 이유는 열 제한이 넘치지 않도록하는 것이지만 실제로 열 제한에 맞는 코드도 작성자의 재량에 따라 줄 바꿈 될 수 있음.
- 줄바꿈을 하기전, 메소드 추출을 이용해도 됨
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) {
여러 문장이 연속해서 내려올 경우 첫 번째 내려온 문장과 동일한 들여쓰기를 유지
- 클래스 멤버들을 구별하는 데 사용( 메소드, 생성자, 멤버 변수 )
- 멤버변수의 경우, 사이에 코드가 없다면 굳이 공백라인을 넣지 않아도 됨.
- 메소드 내부에서 논리적으로 그룹핑 되는 부분
- 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++) {
//
}
- 열거 형 상수 뒤에 오는 각 쉼표 뒤에 줄 바꿈은 선택 사항.
- 추가 빈 줄 (일반적으로 하나만)도 허용됨
private enum Answer {
YES {
@Override public String toString() {
return "yes";
}
},
NO,
MAYBE
}
- 메서드가없고 상수가 없는 열거 형 클래스는 선택적으로 배열 이니셜 라이저 인 것처럼 형식화 될 수 있음
private enum Suit { CLUBS, HEARTS, SPADES, DIAMONDS }
- 모든 변수 선언 (필드 또는 로컬)은 하나의 변수만 선언 e.g. int a, b; //사용되지 않음
int a;
int b;
무조건적으로 시작부분에 시작하는 것은 아님
- 지역 변수는 범위를 최소화하기 위해 처음 사용되는 지점에 가깝게 선언됨
- 지역 변수 선언에는 일반적으로 이니셜 라이저가 있거나 선언 직후에 초기화됨.
- 밑의 형식이 다가능함. (전체 목록이 아님)
new int[] { new int[] {
0, 1, 2, 3 0,
} 1,
2,
new int[] { 3,
0, 1, }
2, 3
} new int[]
{0, 1, 2, 3}
- 대괄호는 변수가 아닌 유형의 일부를 형성.
- String args []가 아니라 String [] args로 선언.
- switch문 자체가 레거시일 수 있으니, 안 알아 봄.
- 클래스, 메서드 또는 생성자에 적용되는 애노테이션은 문서 블록 바로 뒤에 나타나며, 각 애노테이션은 자체 줄에 나열됨. (즉, 한 줄에 하나의 주석)
@Override
@Nullable
public String getNameIfPresent() { ... }
@Override public int hashCode() { ... }
@Partial @Mock DataLoader loader;
/*
* This is // And so /* Or you can
* okay. // is this. * even do this. */
*/
public protected private abstract default static final transient volatile synchronized native strictfp
3000000000l 이 아닌 3000000000L 사용
- 모든 식별자들은 ASCII와 숫자 값만 사용해야 함.
- 식별자에 prefix나 suffixes는 사용하지 않음. 즉, 아래와 같은 형태의 식별자는 사용되지 않음.
name_
mName
s_name
kName
- 모두 소문자로 기술. 단어가 달라지더라도 무조건 소문자를 사용.
com.example.deepspace (O)
com.example.deepSpace (X)
com.example.deep_space (X)
- UpperCarmelCase를 사용. 이는 대문자로 시작하고 단어가 바뀔 때마다 다시 대문자로 표시. e.g. HelloWorld.java
- 테스트 클래스의 경우 마지막에 Test로 끝나도록 함. (HashIntegrationTest)
- lowerCarmelCase를 사용. 이는 소문자로 시작하고 단어가 바뀔 때마다 다시 대문자로 표시. e.g. printHelloWorld()
- 테스트 클래스의 경우 마지막에 Test로 끝나도록 함. (HashIntegrationTest)
- 상수
-- 내용이 완전히 불변하고 메서드에 감지 가능하고 사이드 이펙트가 없는 정적 최종 필드.
-- 여기에는 프리미티브, 문자열, 불변 유형 및 불변 유형의 불변 컬렉션이 포함.
- 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"};
- lowerCarmerCase를 사용.
- 메소드명과 다른 점은 동사가 아닌 명사라는 점. 한문자는 피함.
- 오버라이딩이 된 모든 경우에@Override는 필수로 기입
- 부모 클래스의 메소드를 재정의 하거나 인터페이스를 구현했을 때도 마찬가지.
- 다만 부모 쪽에서 @Deprecated를 선언했을 경우에는 자식 쪽에서 @Override를 생략가능.
- 모든 예외는 무시하지 말고 처리.
- 만약 예외를 처리하지 않을 거면 그 이유에 대해서 명확하게 주석을 담
- 테스트 코드에서는 필요시 무시 가능.
- 클래스 명으로 접근.
Foo aFoo = ...;
Foo.aStaticMethod(); // good
aFoo.aStaticMethod(); // bad
somethingThatYieldsAFoo().aStaticMethod(); // very bad
/**
* 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에 이렇게 많은 규약이 있는지 몰랐습니다.
협업을 진행하면서 꼭 생각을 하며 코드를 작성해야겠습니다.