Etc/개발 팁

Medium - 자바 개발자가 피해야 하는 11가지 실수

요술공주밍키 2024. 5. 29. 16:00

1. Nulls와 Optionals

Bad Practice: 메서드에서 바로 null을 리턴하는 건 NPE를 유발할 수 있습니다.

public String getString() {
    return null;
}

 

Good Practice: null에 대한 명확한 핸들링과 에러 방지를 위해 Optional을 사용합니다.

public Optional<String> getString() {
    return Optional.empty();
}

 


2. String.valueOf()로 String 변환

Bad Practice: '+' 연산자를 사용해 문자열을 합칩니다.

double d = 3.14525;
String s = "" + d;

 

Good Practice: 내장 메서드를 활용합니다.

double d = 3.14245;
String s = String.valueOf(d);

 

3. array를 복사할 때 Arrays.copyOf()

Bad Practice: 정석적으로 array를 복사합니다.

int[] sourceArray = {1, 2, 3, 4, 5};
int[] targetArray = new int[source.length; i++];
for (int i = 0; i < sourceArray.length; i++) {
    targetArray[i] = sourceArray[i];
}

 

Good Practice: Arrays.copyOf() 메서드를 사용합니다.

int[] sourceArray = {1, 2, 3, 4, 5};
int[] targetArray = Arrays.copyOf(sourceArray, sourceArray.length);

 

4. 컬렉션이 비어있는지 체크하기 위해 isEmpty()

Bad Practice: length()나 size()로 string이나 컬렉션이 비어있는지 체크합니다.

String text = "Hello, World!";
if (text.length() == 0) {
    // Do something
}

 

Good Practice: isEmpty()로 string이나 컬렉션이 비어있는지 체크합니다.

String text = "Hello, World!";
if (text.isEmpty()) {
    // Do something
}

 

5. Concurrent Modification Exception 방지

Bad Practice: 리스트를 순회하고 있는 동안 요소를 제거하여 ConcurrentModificatoinException을 일으킵니다.

List<String> words = new ArrayList<>();
words.add("A");
words.add("B");
words.add("C");

for (String word : words) {
    if (word.equals("A")) {
        words.remove(word);
    }
}

 

Good Practice: iterator remove 메서드나 removeIf() 메서드를 사용합니다.

List<String> words = new ArrayList<>();
words.add("A");
words.add("B");
words.add("C");

Iterator<String> iterator = words.iterator();
while (iterator.hasNext()) {
    String word = iterator.next();
    if (word.equals("A")) {
        iterator.remove();
    }
}

 


6. 정규표현식 사전 컴파일

Bad Practice: 런타임 시에 정규표현식을 컴파일합니다.

String text = "Hello, World!";
if (text.matches("Hello.*")) {
    System.out.println("Matches!");
}
String replaced = text.replaceAll("\\s", "");

 

Good Practice: 정규표현식을 사전컴파일하고 재사용합니다.

final Pattern PATTERN1 = Pattern.compile("Hello.*");
final Pattern PATTERN2 = Pattern.compile("\\s");

String text = "Hello, World!";
if (PATTERN1.matcher(text).matches()) {
    System.out.println("Matches!");
}
String replaced = PATTERN2.matcher(text).replaceAll("");

 


7. 검색 전 데이터 존재 여부를 확인하지 않기

Bad Practice: 객체를 가져오기 전 map에 id가 있는지 미리 확인합니다. 존재하지 않는다면 null 값을 반환하기 때문에 미리 체크하는 것은 불필요합니다.

public static String findNameById(Map<Integer, String> idNameMap, int id) {
    if (idNameMap.containsKey(id)) {
        return idNameMap.get(id);
    } else {
        return "Unknown";
    }
}

 

Good Practice: 바로 map에서 가져온 후 null인지 아닌지 체크하여 존재 여부를 확인합니다.

public static String findNameById(Map<Integer, String> idNameMap, int id) {
    String name = idNameMap.get(id);
    if (name != null) {
        return name;
    } else {
        return "Unknown";
    }
}

 


8. 배열의 효율적인 변환

Bad Practice: 

- 리스트의 사이즈가 먼저 계산된 후 새로운 배열이 생성됩니다.

- 큰 컬렉션일 수록 성능에 영향을 미칠 수 있습니다.

List<String> stringList = new ArrayList<>();
stringList.add("apple");
stringList.add("banana");
stringList.add("orange");

String[] array = stringList.toArray(new String[stringList.size()]);

 

Good Practice: 

- 빈 배열과 함께 toArray 메서드가 호출됩니다.

- 리스트의 크기를 계산할 필요가 없으며 toArray 메서드가 내부적으로 배열 크기를 조정할 수 있으므로 성능이 향상되고 코드가 깨끗해집니다.

List<String> stringList = new ArrayList<>();
stringList.add("apple");
stringList.add("banana");
stringList.add("orange");

String[] array = stringList.toArray(new String[0]);

9. 기본 메서드 사용

Bad Practice: logError와 같은 새로운 메서드를 인터페이스에 추가해야 하는 경우 모든 구현 클래스를 수정해야 하므로 코드 관리 문제가 발생할 수 있습니다.

interface Logger {
	void log(String message);
}

class FileLogger implements Logger {
	@Override
    public void log(String message) {
    	System.out.println("Logging to file: " + message);
    }
}

class ConsoleLogger implements Logger {
	@Override
    public void log(String message) {
    	System.out.println("Logging to file: " + message);
    }
}

 

Good Practice: 인터페이스가 기본 메서드를 정의하여 이 메서드는 로그 오류에 대한 기본 구현을 제공합니다. 구현 클래스는 수정할 필요 없이 이 기본 구현을 자동으로 상속합니다.

interface Logger {
	default void log(String message) {
    	System.out.println("Logging to file: " + message);
    }
}

class FileLogger implements Logger {
	@Override
    public void log(String message);
}

class ConsoleLogger implements Logger {
	@Override
    public void log(String message);
}

 

10. Date/Time API 사용

Bad Practice: 레거시 Date 클래스를 사용합니다.

- 이 클래스는 변형 가능성, 메서드 명확성 부족 등 다양한 문제가 있습니다.

- getYear(), getMonth()와 같은 이 클래스의 대부분의 메서드는 사용되지 않습니다.

import java.util.Date;

public class DateUtil {
    public static void main(String[] args) {
        Date currentDate = new Date();
        System.out.println("Current date: " + currentDate);
    }
}

 

Good Practice: Date/Time API를 사용합니다.

import java.time.LocalDate;

public class DateUtil {
    public static void main(String[] args) {
        LocalDate currentDate = LocalDate.now();
        System.out.println("Current date: " + currentDate);
    }
}

11. 제네릭 사용

Bad Practice: 다른 타입의 데이터가 리스트에 혼합되어 저장됩니다. 런타임 에러를 유발할 수 있습니다.

ArrayList list = new ArrayList();
list.add(10);
list.add("Hello");

 

Good Practice: 타입의 안정성을 보장하는 제네릭을 사용합니다.

ArrayList list = new ArrayList<>();
list.add(10);
// list.add("Hello"); // Compile-time error: incompatible types

 

출처

https://levelup.gitconnected.com/15-11-mistakes-every-java-developer-must-avoid-today-ccd7e681a970