본문 바로가기

PMD

[한글화 시리즈-01] PMD Basic Rules(1)

Basic Rules(1)

이 룰은 개발자가 적용할만한 좋은 예들로 구성되어있다.



EmptyCatchBlock

이 룰은 비어 있는 catch절에 관한 룰이며, 대부분의 환경에서 비어있는 catch절은 꼭 처리되거나 보고되어야할 오류들을 단순히 무시해버릴 가능성이 있다.

public void doSomething() {
  try {
    FileInputStream fis = new FileInputStream("/tmp/bugger");
  } catch (IOException ioe) {
      // 이곳이 비어 있는 것은 좋지 않다.
  }
}


EmptyIfStmt

이 룰은 비어 있는 조건절을 찾아낸다. 

public class Foo {
 void bar(int x) {
  if (x == 0) {
   // 비어있는 조건절!
  }
 }
}


EmptyWhileStmt

이 룰은 비어 있는 while절을 찾아낸다. 만약 이 비어있는 while절이 시간조절(timing)용으로 쓰인다면 Thread.sleep() 을 사용하는 것이 더 나은 방법이며, 이런 loop는 많은 실행비용을 발생시킨다.

public class Foo {
 void bar(int a, int b) {
  while (a == b) {
   // 비어있어!
  }
 }
}


EmptyTryBlock

비어 있는 try절을 피해야한다.

public class Foo {
 public void bar() {
  try {
  } catch (Exception e) {
    e.printStackTrace();
  }
 }
}


EmptyFinallyBlock

비어 있는 finally절을 피해야하며, 이런 구문은 삭제될 수 있다.

public class Foo {
 public void bar() {
  try {
    int x=2;
   } finally {
    // 비어있다!
   }
 }
}


EmptySwitchStatements

비어 있는 switch절을 피해야한다.

public class Foo {
 public void bar() {
  int x = 2;
  switch (x) {
   // 코드가 있었지만
   // 삭제되거나 주석처리로 사라짐.
  }
 }
}


JumbledIncrementer

무질서한 반복문 변수(increamenter)는 일반적으로 개발자가 실수 할 수 있으며, 비슷한 다른 변수로 혼동될 수 있다.

public class JumbledIncrementerRule1 {
  public void foo() {
   for (int i = 0; i < 10; i++) {
    for (int k = 0; k < 20; i++) {
     System.out.println("Hello");
    }
   }
  }
 }


ForLoopShouldBeWhileLoop

특정 for문은 while문으로 간결하게 만들 수 있다.

public class Foo { void bar() { for (;true;) true; // 초기화 및 증가 부분이 없는 for문의 경우 while(true)로 수정하는 것이 나은 방법이다. } }


UnnecessaryConversionTemporary

변수형 변환을 위한 불필요한 String 같은 임시 변수 생성을 피하라.

public String convert(int x) {
  // this wastes an object
  String foo = new Integer(x).toString();
  // this is better
  return Integer.toString(x);
}


OverrideBothEqualsAndHashcode

클래스에 equals 메소드(method) 또는 hashCode 메소드를 override할 경우 두 메소드들 모두 override하거나 아니면 모두 하지 않아야 한다. 기본적으로 equals은 hashCode를 이용하여 같은 객체라는 비교를 하므로, 만약 hashCode 메소드를 부모 클래스로부터 상속받는다고하여도, 해당 클래스에 hashCode 메소드 구현을 고려해야하며 명확하게 슈퍼 클래스(super class)로 위임해야한다.

// hashCode 메소드가 필요하다 public class Bar { public boolean equals(Object o) { // 객체 비교... } } // equals 메소드 필요 public class Baz { public int hashCode() { // hash code 반환 } } // 정상적인 방식 public class Foo { public boolean equals(Object other) { // 객체 비교... } public int hashCode() { // hash code 반환 } }


DoubleCheckedLocking

스레드(thread) 동기화를 위한 코드 중 잘못된 중복 객체 확인은 불완전하게 부분적으로 생성된 객체를 반환할 수 있다. 아래와 같이 synchronized를 이용하여 스레드 동기화를 할 경우, 잘못된 객체를 호출할 가능성이 있다.

  1. 일반적인 영역에서 클래스 맴버 객체를 확인했을 경우 객체가 null이면 객체를 등록하기 위하여 동기화 블록으로 진입한다.
  2. 동기화 블록 내에서 다시 한번 맴버 객체가 null인지 확인하고 객체를 초기화한다.
  3. 2의 과정이 진행되는 동안 객체는 null인 상태가 아니므로 누군가 객체를 호출할 경우, 생성중인 불완전한 객체를 참조할 수 있다.

public class Foo { Object baz; Object bar() {

if(baz == null) { //baz는 null은 아니지만 완벽하게 작동하지는 않을 가능성이 있다. synchronized(this){ 

if(baz == null){

baz = new Object(); } } } return baz; } }


ReturnFromFinallyBlock

finally에서는 값을 반환하지 않는다. exception들이 버려질 수 있다.

public class Bar { public String foo() { try { throw new Exception( "My Exception" ); } catch (Exception e) { throw e; } finally { return "A. O. K."; // 매우 나쁘다! } } }


EmptySynchronizedBlock

비어있는 스레드 동기화절(synchronized blocks)을 불필요하다.

public class Foo {
 public void bar() {
  synchronized (this) {
   // 비어있다!
  }
 }
}


UnnecessaryReturn

불필요한 return문은 피해라. void와 같은 경우 return을 할 이유가 없다.

public class Foo {
 public void bar() {
  int x = 42;
  return;
 }
}


EmptyStaticInitializer

비어있는 static 초기화 블럭을 피해라.

public class Foo {
 static {
  // 비어있다!
 }
}


UnconditionalIfStatement

조건절에 비교문이 들어가지 않고 단순히 true 또는 false가 되는 경우 if문을 사용하지 말자.

public class Foo {
 public void close() {
  if (true) {
       // ...
   }
 }
}


EmptyStatementNotInLoop

비어있는 구문(; <- 세미콜론(semicolon)으로 알려진)은 반복문 내에서는 두개의 ;(세미콜론)을 포함하고 있어야 하며, 하나만 나타날 경우 이는 버그일 가능성이 있다. 또한 코드상 ;(세미콜론) 혼자 나타거나 문장 끝에 중복되어 나타날 경우 불필요한 부분으로 삭제하여야 한다.

public class MyClass { public void doit() { // 아래와 같은 경우는 아마 개발자가 원치 않는 경우이다. ; // 다음과 같은 중복된 ;(세미콜론)은 불필요하다. System.out.println("look at the extra semicolon");; } }


BooleanInstantiation

Boolean과 같은 경우 Boolean 객체로 인스턴트화할 필요 없이 new Boolean() 또는  Boolean.valueOf() 로 인스턴스화 할 필요 없이 Boolean.TRUE 또는 Boolean.FALSE로 대체 될 수 있다. 


public class Foo { Boolean bar = new Boolean("true"); // 그냥 다음과 같이 해라 Boolean bar = Boolean.TRUE; Boolean buz = Boolean.valueOf(false); // 그냥 다음과 같이 해라 Boolean buz = Boolean.FALSE; }


UnnecessaryFinalModifier

클래스가 final 선언을 갖고 있다면, 모든 메소드(method)들은 자동으로 final로 등록됨으로 불필요하게 모든 메소드에 final을 사용할 필요 없다.

public final class Foo { // 클래스가 final로 설정되어 있음으로 아래와 같이 메소드를 final로 설정할 필요가 없다. private final void foo() { } }


CollapsibleIfStatements

조건절 내의 조건문들은 || 또는 &&를 사용하여 하나의 조건절 내에서 사용될 수 있다.

public class Foo {
 void bar() {
  //비추천
  if (x) {
   if (y) {
    ...
   }
  }
  //위와 동일하다.
  if(x && y) {
    ...
  }
 }
}


UselessOverridingMethod

일반적으로 슈퍼클래스의 같은 메소드를 단순히 호출하는 메소드는 오버라이딩 할 필요가 없다, 하지만 annotation을 붙여야 하는 경우, 또는 superclass의 메소드가 private 또는 protected로 접근제한되어 있어서 외부에서 public으로 호출해야하는 아주 특별한 경우에 조심스럽게 사용할 수 있다.

public void foo(String bar) {
    super.foo(bar);      //왜 오버라이딩을 하지?
}
             
public String foo() {
    return super.foo();  //왜 오버라이딩을 하지?
}

@Id
public Long getId() {
    return super.getId();  //annotation을 붙여야 할 경우 가능하다
}
   


ClassCastExceptionWithToArray

List와 같은 collection에서 객체를 array형태로 얻고 싶을 경우 원하는 객체형의 array를 toArray 메소드의 파라메터(parameter)로  전달해야한다. 그렇지 않을 경우 ClassCastException이 발생한다.

import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) { Collection c=new ArrayList(); Integer obj=new Integer(1); c.add(obj); // 아래와 같은 경우 실행시에 ClassCastException을 발생한다.
Integer[] a=(Integer [])c.toArray(); // 아래와 같은 경우 정상적으로 작동한다. 단 class의 형과 사이즈를 꼭 포함해야한다. Integer[] b=(Integer [])c.toArray(new Integer[c.size()]); } }


AvoidDecimalLiteralsInBigDecimalConstructor

new BigDecimal(.1).1과 정확하게 같을 것으로 추정할 수 있지만, 하지만 실제로는 .100000000000000005551 1151231257827021181583404541015625과 같다. 그 이유는, .1은 정확하게 double 형으로 표현될 수 없기 때문이다. 그러므로 BigDecimal을 생성할 경우 생성자에 숫자형의 값이 전달될 경우 정확하게 .1이 나올 보장이 없다. 하지만 String형태의 값을 전달할 경우 정확한 BigDecimal을 생성할 수 있으므로, String을 이용하여 BigDecimal을 초기화하자.

import java.math.BigDecimal; public class Test { public static void main(String[] args) { // 좋지 않은 경우다. BigDecimal bd=new BigDecimal(1.123); // 추천되는 생성 방식 BigDecimal bd=new BigDecimal("1.123"); // 소수점 아래 단위가 없음으로 가능한 방식 BigDecimal bd=new BigDecimal(12); } }





해당 URL: http://pmd.sourceforge.net/pmd-4.2.6/rules/basic.html