미리 알았다면 좋았을 자바 팁

김민창·2024년 5월 19일
0
post-thumbnail

여기의 내용을 번역하여 정리했습니다

Null 반환을 조심하라

  • 우리는 Java를 사용할때, nullpointerexception 의 굴레에서 살아간다.
  • 특정 메서드의 리턴값이 null 을 반환하기 보다는 Optional을 사용하자
Bad Practice
public String getString(){
	return null;
}
Good Practice
public Optional<String> getString(){
	return Optional.empty();
}

String 자료형으로 변환

  • + 연산자를 통하여 특정 자료형을 String 으로 변환할수도 있다
  • 하지만 이러한 방식은 많은 수의 변수를 문자열로 변환할 때 비효율적이니 내장 메서드를 사용하자
Bad Practice
double d = 3.141592;
String s = "" + d;
Good Practice
double d = 3.141592;
String s = String.valueOf(d);

배열을 복사할때도 스마트하게

  • 배열 길이를 할당하고, 모든 인덱스에 반복문을 돌며 하나씩 값을 넣어주는게 아니라 만들어져있는 메서드를 사용하자.
  • 코드적으로도 이해하기 쉽다
Bad Practice
int[] = sourceArray = {1, 2, 3, 4, 5};
int[] = targetArray = new int[sourceArray.length]; // 배열 길이 할당
for (int i = 0; i < sourceArray.length; i++){
	targetArray[i] = sourceArray[i];
}
Good Practice
int[] originalArray = {1, 2, 3, 4, 5};
int[] copiedArray = Arrays.copyOf(originalArray, originalArray.length);

자료형의 상태를 확인할 수 있는 메서드를 사용하자

  • 리스트가 비어있는지, 문자열이 존재하는지 등 자료형의 상태를 어떻게 확인을 하고있나?
  • 리스트가 비어있는지 if(list.size() == 0) 와 같이 확인하기 보다는 해당 자료형에서 상태값을 확인할 수 있는 메서드가 있다
Bad Practice
String text = "Hello!";
if(text.length() == 0){
	// do
}

List<Long> numbers = new ArrayList<>();
if(numbers.size() == 0){
	// do
}
Good Practice
String text = "Hello!";
if(text.isEmpty()){
	// do
}

List<Long> numbers = new ArrayList<>();
if(numbers.isEmpty()){
	// do
}

리스트에 대한 수정을 조심

  • 다음과 같이 리스트를 순회하며 수정하는 로직은 ConcurrentModificationException 에러를 만날 수 있다
Bad Practice
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);
    }
}

  • ArrayList 객체 내부를 확인해보면 엘리먼트의 추가 및 삭제마다 modCount 가 1씩 증가하게 되는데, next()를 호출할때마다 다음 checkForComodification 함수를 호출하게 된다.
  • modCount 는 배열의 수정으로 인하여 expectedModCount 와 달라졌기 때문에 ConcurrentModificationException 에러가 발생하는것
  • 좀더 자세한 설명은 여기로~

Good Practice
  • iterator 에서 지원하는 remove 함수의 사용을 권장!
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();
    }
}
  • 혹은 간단하게 removeIf 함수의 사용하는 법도 있겠다. (자바8 이상)
List<String> words = new ArrayList<>();
words.add("A");
words.add("B");
words.add("C");

words.removeIf(word -> word.equals("A"));

정규 표현식은 미리 정의하기

  • 반복되는 정규 표현식들을 사용할때마다 사용하지않고 미리 정의하자
Bad Practice
String text = "Hello!";
if (text.matches("Hello.*")) {
	System.out.println("Matched!");
}
String replaced = text.replaceAll("\\s", "");
Good Practice
final Pattern PATTERN1 = Pattern.compile("Hello.*");
final Pattern PATTERN2 = Pattern.compile("\\s");

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

검색하기 전 미리 데이터의 존재 여부를 확인하지 마라

  • 바로 코드를 본다면
Bad Practice
public static String findNameById(Map<Integer, String> idNameMap, int id){
	if (idNameMap.containsKey(id)){
		return idNameMap.get(id);
	}
	else {
		return "Unknown";
	}
}
  • Map 에서 내가 원하는 데이터가 있는지 존재여부 확인 후, 내가 원하는 데이터를 반환하는 로직으로 확인이 가능하다.
  • 하지만 이런 코드는 중복된 로직으로써 다음과 같이 수정 가능하다
Good Practice
public static String findNameById(Map<Integer, String> idNameMap, int id){
	String name = idNameMap.get(id);
	if (name != null){
		return name;
	}
	else {
		return "Unknown";
	}
}

컬렉션을 배열로 변경하기

Bad Practice
  • 내부 배열을 선언할때 컬렉션의 크기를 직접 지정해주고 있다
List<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");

String[] array = stringList.toArray(new String[stringList.size()]);
Good Practice
  • 하지만 컬렉션의 toArray 내부를 확인해보면 다음과 같이 선언되어 있어, 크기 조정을 직접 해줄 필요가 없다는걸 알 수 있다.
List<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");

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

디폴트 메서드도 사용하자

  • 만약 내가 오픈소스를 만들었는데 너무 잘 만들어서 전 세계 사람들이 사용하고 있다고 하자. 그런데 인터페이스에서 필수적으로 구현해줘야할 메서드가 생겨버린다면 ?
  • 인터페이스에 메서드를 추가뒤 배포했다. 내 오픈소스를 사용하는 사람들은 버전업을 함과 동시에 추가된 메서드를 구현해야한다...(죄송)
  • 그럴때 구현할 필요없이 바로 사용할 수 있는 디폴트 메서드 잠수함패치!
Bad Practice
interface Logger {
	void log(String message);
}
Good Practice
interface Logger {
	void log(String message);
    
    default void logError(String errorMessage){
    	System.err.println("Error: " + errorMessage);
    }
}

Date 클래스는 이제 놓아주자

  • LocalDate, LocalDateTime 이 훨씬 직관적이다
Bad Practice
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
import java.time.LocalDate;

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

제네릭 타입을 써주자

  • 컴파일 시점에 에러를 확인할 수 있게 해주는 제네릭타입을 적극 활용하자
Bac Practice
ArrayList list = new ArrayList();
list.add(10);
list.add("Hello");	// Runtime Error!
Good Practice
ArrayList<Integer> list = new ArrayList();
list.add(10);
list.add("Hello");	// Compile Error!

자바를 사용할때 생각할만한 것들을 정리한 블로그를 읽으면서, 사실상 당연한것들이라고 생각을 했던것들이 대부분이긴 하다.
그래도 생각만 하고있는것보다 한번 정리해보는 기회로 그래도 나쁘진 않은것 같다.

profile
개발자 팡이

0개의 댓글