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

김민창·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개의 댓글

관련 채용 정보