본문 바로가기

PMD

[한글화 시리즈-02] PMD Basic Rules(2)

Basic Rules(2)

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



UselessOperationOnImmutable

이뮤터블객체들(String, BigDecimal 또는 BigInteger 등)에서의 연산(operation)은 스스로를 변경하지 못한다. 이 연산의 결과는 새로운 객체로 생성되어 반환된다. 이런 객체의 연산의 결과가 무시될 경우 에러로 인식한다.

*이뮤터블 객체(Immutable Objects) 한번 만들어진 객체는 변경되지 않으며, 변형이 발생할 때는 새로운 객체를 만든다.

import java.math.*; class Test { void method1() { BigDecimal bd=new BigDecimal(10); bd.add(new BigDecimal(5)); // 연산 결과가 무시된다, } void method2() { BigDecimal bd=new BigDecimal(10); bd = bd.add(new BigDecimal(5)); // 연산 결과가 반영된다. } }


MisplacedNullCheck

잘못된 위치의 null 체크하는 룰. 기본적으로 변수가 null일 경우 변수를 사용할 경우 NullPointerException이 발생하며, 이 룰은 null 체크가 필요 없거나, 잘못된 null 체크에서 발생한다.


public class Foo {
 void bar() {
  //a가 null일 경우 NullPointerException이 발생하며
  //a가 null이 아닐 경우 equals가 실행됨으로 a != null은 불필요한 체크이다.
  if (a.equals(baz) && a != null) {}
 }
}

public class Foo {
 void bar() {
  //a가 null일 경우 NullPointerException이 발생해야하며 equals는 작동하지 않아야한다.
  //그러므로 a == null 과 같은 체크는 잘못된 null 체크이다.
  if (a.equals(baz) || a == null) {}
 }
}   


UnusedNullCheckInEquals

equals 메소드 사용 시에는 해당 객체의 null 체크 후에 사용하는 것이 좋으며, null이 아닌 객체의 equals 를 사용하는 방법이 null일지 아닐지 모르는 다른 객체에 equals 메소드로 null 아닌 객체를 전달하는 것보다 나은 방법이다.

public class Test {

 public String method1() { return "ok";}
 public String method2() { return null;}

 public void method(String a) {
  String b;
  /*
    method1()이 null일 수 있지만
    a가 null이 아니라는 것을 확실하다면
    a.equals(method1())이 더 나은 방법이다.
  */
  //a객체가 null이 아닌 것은 확실하지만
  //method1()은 null인지 아닌지 불분명함으로 
  //다음은 룰 위반이다.
  if (a!=null && method1().equals(a)) { 
    ...
  }
  //equals에 전달된 a가 null인지 아닌지 모르는 상태이고,
  //mehtod1()이 null 이 아닌 상태로 equlas가 실행되었다.
  //그러므로 룰 위반이 아니다.
  if (method1().equals(a) && a != null) { 
    ...
  }

  //a의 null체크와 method1의 equlas가 별개의 조건으로 
  //룰 위반이 아니다.
  if (a!=null && method1().equals(b)) {
    ...
  }

  //a의 null체크와 문자열의 equlas가 별개의 조건으로  
  //룰 위반이 아니다.
  if (a!=null && "LITERAL".equals(a)) {
    ...
  }

  //a의 null 체크를 하였고 
  //null이 아닌 a에 equals 사용하였음으로 룰 위반이 아니다.
  if (a!=null && !a.equals("go")) {
    a=method2();
    //a는 null이지만 null이 아닌 method1의 equals에 전달되었음으로
    //다음은 룰 위반이 아니다.
    if (method1().equals(a)) {
      ...
    }
  }
 }
}


AvoidThreadGroup

java.lang.ThreadGroup 사용을 피하라. 비록 이것이 스레드 환경에서 사용하도록 의도되었다 하더라도, 이것은 스레드에 안전하지 않은  메소드를 포함한다.

 public class Bar {
  void buz() {
    ThreadGroup tg = new ThreadGroup("My threadgroup") ;
    tg = new ThreadGroup(tg, "my thread group");
    tg = Thread.currentThread().getThreadGroup();
    tg = System.getSecurityManager().getThreadGroup();
  }
}


BrokenNullCheck

|| 대신에 &&를 사용하거나 반대로 && 대신에 ||을 사용하여  잘못된 null 체크를 할 경우 NullPointerException이 발생한다.

class Foo {
 String bar(String string) {
  // &&를 사용해야한다.
  // ||를 사용할 경우 string이 null일 수 있다.
  if (string!=null || !string.equals(""))
    return string;
  // ||를 사용해야한다.
  // &&를 사용할 경우 string.equals는 NullPoiterException을 발생시킨다.
  if (string==null && string.equals(""))
    return string;
 }
}


BigIntegerInstantiation

이미 존재하는 다음과 같은 BigInteger 인스턴스들은 생성하지 않는다. 

Java 1.2이상: BigInteger.ZERO, BigInteger.ONE

Java 1.5이상: BigInteger.TEN, BigDecimal (BigDecimal.ZERO, BigDecimal,ONE, BigDecimal.TEN)

public class Test {

 public static void main(String[] args) {
   //BigInteger.ONE을 사용
   BigInteger bi=new BigInteger(1);
   //BigInteger.ZERO를 사용
   BigInteger bi2=new BigInteger("0");
   //BigInteger.ZERO를 사용
   BigInteger bi3=new BigInteger(0.0);
   BigInteger bi4;
   //BigInteger.ZERO를 사용
   bi4=new BigInteger(0);
 }
}


AvoidUsingOctalValues

8진수형 변수값은 사용하지말자.Java의 8진수 값은  0로 시작하는 integer 값이다.

public class Foo {
  int i = 012; // i는 12가 아닌 10이다
  int j = 010; // j는 10이 아닌 8이다.
  k = i * j;   // k는 120이 아닌 80이다.
}


AvoidUsingHardCodedIP

하드 코딩된 IP가 포함된 응용프로그램은 특정 케이스에서 배포가 불가능한 경우가 있다. 그러므로, IP를 코드상 하드코딩하지말자!


public class Foo {
  String ip = "127.0.0.1"; // 진짜 나쁜 코드
}


CheckResultSet

ResultSet의 네비게이션 메소드(navication method)들 next, previous, first, last을 사용할 경우 언제나 return 값을 확인해야한다. 실제로, 이런 메소드들이 'false'를 돌려줄 경우, 프로그래머는 이에대한 처리를 해야한다.

//부적절한 코드
Statement stat = conn.createStatement();
ResultSet rst = stat.executeQuery("SELECT name FROM person");
rst.next(); // 만약 false를 반환한다면 어떻게 하지???
String firstName = rst.getString(1);

//적절한 코드
Statement stat = conn.createStatement();
ResultSet rst = stat.executeQuery("SELECT name FROM person");
if (rst.next()) {
  String firstName = rst.getString(1);
} else {
  // 에러에 대한 로그 처리와 같은 최소한의 처리를 할 수 있다.
}


AvoidMultipleUnaryOperators

복합 단항 연산자 (multiple unary operators)들을 사용할 경우 버그가 발생되거나, 혼란 스럽게 할 수 있다. 이런 사용이 버그가  없도록 확인하거나, 더욱 심플한 표현을 고려해야한다.

// 오자 버그들, 불필요한 복잡성 및 혼동
int i = - -1;
int j = + - +1;
int z = ~~2;
boolean b = !!true;
boolean c = !!!true;

// 더 나은 방법들:
int i = 1;
int j = -1;
int z = 2;
boolean b = true;
boolean c = false;

// 그냥 머리만 아픈 코드
int i = ~-2;
int j = -~7;


EmptyInitializer

빈 초기화를 경고하는 룰

public class Foo {

   static {} // 왜?

   {} // 다시한번, 왜?

}



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