반응형
 이 글은 내가 우아한테크캠프에서 배웠던 내용들을 복습하기 위해 진행하는 토이프로젝트(https://github.com/minseokLim/woowahan-tech-camp-review)를 중심으로 작성되었다.

 

1. Checkstyle

 Checkstyle은 코딩 컨벤션 검사를 위한 도구이다. 자동화된 검사가 가능하며, 설정에 따라 규칙에 어긋나는 코드가 하나라도 있을 경우 빌드가 실패하도록 만들 수도 있다. 사용법은 https://naver.github.io/hackday-conventions-java/#checkstyle 을 참고하면 확인할 수 있다.

 코딩 컨벤션은 기본적으로 https://naver.github.io/hackday-conventions-java 을 따르되 약간의 커스터마이징을 하였다. 커스터마이징 사항은 아래와 같다.

1) 대문자로 표기할 약어를 허용하지 않는다. 즉 DAO와 같은 예외 케이스를 두지 않았다.

2) 메서드명에 한글을 허용하였다. 이는 테스트 코드 작성 시 메서드명을 한글로 만들기 위함이며, 프로덕션 코드에는 허용되지 않는다.

3) 인덴트로 하드탭이 아닌 4 spaces를 사용한다.

4) 줄바꿈줄 바꿈 허용 위치 중 '+' 기호에 한해서만 예외적으로 연산자 후에도 줄 바꿈이 가능하도록 하였다. 이는 IDE가 자동으로 생성해주는 toString() 함수를 매번 수정해주는 것이 너무 불편했기 때문이다.

5) import문 순서에 대한 규칙 중, nhncorp, naver와 같은 네이버 전용 패키지는 배제하였다. 대신 토이프로젝트 어플리케이션 코드에서 사용하는 minseoklim 패키지가 import문 중 가장 마지막에 위치하도록 하였다.

 

2. Lombok

 롬복은 어노테이션을 통해 반복적인 코드를 자동으로 생성해주는 도구이다. 이를 활용하면 코드를 더 깔끔하게 유지할 수 있다는 장점이 있다. 하지만 편의성만큼이나 단점도 존재한다. 따라서 주의해서 사용해야 하며 때에 따라서는 아예 사용을 금지하는 편이 좋은 것도 있다. 이에 대해서는 https://kwonnam.pe.kr/wiki/java/lombok/pitfall 에 잘 정리되어 있다. 

 lombok.config을 사용하면 특정 롬복 어노테이션 사용 시 오류가 발생하도록 하여 사용을 못하게 강제할 수 있다. 사용법은 간단하다. 프로젝트 최상단에 아래와 같이 lombok.config 파일을 생성한다.

 이후 lombok.config 파일 안에 아래와 같이 사용을 금지할 어노테이션을 지정해주면 된다.

lombok.allArgsConstructor.flagUsage = error
lombok.requiredArgsConstructor.flagUsage = error
lombok.data.flagUsage = error
lombok.value.flagUsage = error
lombok.experimental.flagUsage = error
lombok.toString.flagUsage = error

 

 @EqualsAndHashCode의 경우 불변 객체에 대해서만 사용해줘야 하는데, 사실 이건 롬복의 문제라기 보단 불변 객체에 대해서만 equals()와 hashcode()를 재정의해주는 편이 좋다는 것이다.

 

※ 참고

https://naver.github.io/hackday-conventions-java

 

캠퍼스 핵데이 Java 코딩 컨벤션

중괄호({,}) 는 클래스, 메서드, 제어문의 블럭을 구분한다. 5.1. K&R 스타일로 중괄호 선언 클래스 선언, 메서드 선언, 조건/반복문 등의 코드 블럭을 감싸는 중괄호에 적용되는 규칙이다. 중괄호

naver.github.io

https://kwonnam.pe.kr/wiki/java/lombok/pitfall

 

java:lombok:pitfall [권남]

 

kwonnam.pe.kr

 

반응형
반응형

 우아한테크캠프를 수료한 지 벌써 3달이 다 되어간다. 총 9주(2021년 11월 1일 ~ 2021년 12월 31일) 과정으로 진행되었으며 정말 유익한 시간이었다. 물론 9주라는 짧은 시간 동안 얼마나 많은 성장을 할 수 있었겠냐만은, 백엔드 개발자로서 어떻게 성장해야 하는지, 그리고 어떤 방향으로 나아가야 하는 지를 깨닫게 해주는 소중한 경험이었다.

 그 이후로는 개인적으로 좀 지치기도 해서 쉬는 시간이 필요했다. 바쁘다는 핑계로 소홀했던 가족들, 여자친구와 더 많은 시간을 보냈고, 개발자로서 앞으로 어떻게 살아가야 할지를 생각하는 시간도 가졌다. 그러다 보니 우아한테크캠프에서 배웠던 내용들을 복습하는데 시간이 좀 걸렸다^^;;

 이 포스팅은 내가 우아한테크캠프에서 배웠던 내용들을 바탕으로 진행하는 토이프로젝트(https://github.com/minseokLim/woowahan-tech-camp-review)를 중심으로 진행될 것이다. 우아한테크캠프에서 배웠던 내용들을 기준으로 진행할 것이나, 중간중간 내가 추가하고 싶은 내용이 포함될 수도 있다.

 

 아래의 내용은 우아한테크캠프를 수강하면서 나왔던 요구사항들을 정리한 것이다. 실무에서 이 모든 것들을 지키는 것은 불가능할 것이고 실용적이지도 않다. 하지만 공부하는 과정에서 아래의 다소 극단적인 요구사항들을 지키려고 노력한다면, 그 과정에서 새로운 인사이트를 얻을 수 있을 것이다.

 

# 우아한테크캠프 Pro 내재화 프로젝트

## 프로그래밍 요구사항
* 객체지향 생활 체조 원칙
  * 규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 한다.
  * 규칙 2: else 예약어를 쓰지 않는다.
  * 규칙 3: 모든 원시값과 문자열을 포장한다.
  * 규칙 4: 한 줄에 점을 하나만 찍는다.
  * 규칙 5: 줄여쓰지 않는다(축약 금지).
  * 규칙 6: 모든 엔티티를 작게 유지한다.
  * 규칙 7: 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  * 규칙 8: 일급 콜렉션을 쓴다.
  * 규칙 9: 게터/세터/프로퍼티를 쓰지 않는다.
* TDD
  * 원칙 1: 실패하는 단위 테스트를 작성할 때까지 프로덕션 코드(production code)를 작성하지 않는다.
  * 원칙 2: 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다.
  * 원칙 3: 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다.
* ATDD
  * 코딩을 시작하기 전에 인수 테스트를 먼저 작성한다.
  * 이 프로젝트에서의 인수 테스트는 API 접점에서 코드를 검증하는 E2E 테스트로 한다.
  * API의 Request와 Response 정보 이외의 내부 정보는 최대한 가리는 블랙박스 테스트 형식을 취한다.
  * 인수 테스트 코드는 사용자 스토리를 시나리오 형식으로 표현하여 작성한다.
* 기타
  * 메소드의 길이가 15라인을 넘어가지 않도록 구현한다.
  * 메소드/클래스 분리(SRP)
  * 값을 하드코딩하지 않는다.
  * 필드에 직접 접근하기보단 메시지 방식을 취한다(enum 포함).
  * 자바 코드 컨벤션을 지키면서 프로그래밍한다.
  * 불필요하게 공백 라인을 만들지 않는다.

## 기능 목록 및 commit 로그 요구사항
* 기능을 구현하기 전에 README.md 파일에 구현할 기능 목록을 정리해 추가한다. 이때, 예외 상황에 대해서도 정리한다.
* git의 commit 단위는 앞 단계에서 README.md 파일에 정리한 기능 목록 단위로 추가한다.
* 커밋 메시지는 의미있게 작성한다.

 

반응형
반응형

일반적으로 intellij와 같은 IDE를 통해 파일명을 변경하게 되면, 아래와 같이 '이름 바꿈'으로 GIT이 인식하도록 IDE가 알아서 처리를 해준다. 이는 IDE가 일반적인 리네임 방법인 mv가 아니라 git mv 를 사용하기 때문이다.

헌데 필자는 현재까지의 작업 상황을 잠시 stash 한 후 다시 pop하여 사용할 일이 생겼다. 그랬더니 아래와 같은 상황이 벌어졌다-_-

이렇게 되니, 현재까지의 작업 내용을 확인할 때 이름만 변경한 건 무엇이고 실제로 새로 추가한 파일은 무엇인지 파악하기 힘들어졌다. 구글링 끝에 아래와 같은 명령어를 이용하면 된다는 걸 알게 되었다.

git commit --dry-run -a

 

여담으로, git status를 통해 새 파일과 삭제한 파일로 인식되던 것도 git add를 하게되면 알아서 다시 이름 바꾼 상태라는 걸 git이 인식하게 된다. 따라서 위 명령어는 add 하기 이전에 작업 상황을 파악하는데 사용하면 좋을 것 같다.

 

- 참고 : https://stackoverflow.com/questions/2641146/handling-file-renames-in-git

반응형
반응형

처음엔 1, 11, 111... 이런식으로 수를 늘려가면서 수를 나눠본 후, 처음 나누어 떨어졌을 때의 자리수를 구하면 된다고 생각했다. 코드는 아래와 같다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringJoiner;

public class Main {
    public static void main(String[] args) throws IOException {
        final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        final StringJoiner result = new StringJoiner(System.lineSeparator());

        String input;
        while ((input = br.readLine()) != null) {
            final int n = Integer.parseInt(input);
            result.add(String.valueOf(computeAnswer(n)));
        }

        System.out.println(result);
    }

    private static int computeAnswer(int n) {
        long multiplyer = 1;
        int count = 1;

        while (multiplyer % n != 0) {
            multiplyer = multiplyer * 10 + 1;
            count++;
        }

        return count;
    }
}

하지만 이 코드는 시간초과로 실패했다. 이것 저것 시도를 해보다가 결국 포기하고 구글링을 했다.

 

이 문제는 아래의 나머지 성질을 이용해서 풀 수 있다.

(A + B) % C = (A % C + B % C) % C
(A * B) % C = ((A % C) * (B % C)) % C

이것은 수학 시간에 배우던 분배법칙과 비슷하다. 하지만 '%'라는 기호는 프로그래밍을 하면서 처음 알게되었고 증명이 필요했다.

아... 근데 막상 이 공식을 통해서 어떻게 multiplyer = (multiplyer * 10 + 1) % n 으로 적용해도 된다는 걸 알 수 있는 건지 모르겠다. 아무리 봐도 모르겠네...ㅠ 내가 수학을 하는 건지 코딩을 하고 있는 건지...-_-;; 아무튼 그래서 더 간단한 방법으로 증명하는 건 불가능할까 고민하다가 아래와 같이 증명해봤다.

이렇게 하면, 10A + 1 을 B로 나눴을 때의 나머지가 10R + 1을 B로 나눴을 때의 나머지와 같다는 걸 쉽게 알 수 있다. 비단 10과 1에 국한되진 않을 거다. 아무튼 이 결과를 적용한 코드는 아래와 같다.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringJoiner;

public class Main {
    public static void main(String[] args) throws IOException {
        final BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        final StringJoiner result = new StringJoiner(System.lineSeparator());

        String input;
        while ((input = br.readLine()) != null) {
            final int n = Integer.parseInt(input);
            result.add(String.valueOf(computeAnswer(n)));
        }

        System.out.println(result);
    }

    private static int computeAnswer(int n) {
        int multiplyer = 1;
        int count = 1;

        while (multiplyer % n != 0) {
            multiplyer = (multiplyer * 10 + 1) % n;
            count++;
        }

        return count;
    }
}

 

이 문제를 통해 알게된 정말 놀라운 사실은, 기본형에 해당하는 수의 크기를 줄이는 것으로 속도를 향상시킬 수 있다는 것이었다. 연산에 드는 시간이 줄긴하겠지만, 이렇게나 차이가 날 줄이야...;

 

 

※ 참고 : https://dynamiccube.tistory.com/4

반응형
반응형

 요즘은 이력서를 쓸 때 Git과 블로그가 거의 필수라고 한다. 그만큼 지속적으로 블로그에 글을 남기고 운영하는 게 취업에 도움이 된단 얘기겠지. 이 블로그를 처음 만든 것도 같은 이유였다. 더 좋은 회사에 취업하는 데 도움이 될 것 같았다. 하지만 지속적으로 글을 남기는 데는 번번이 실패하고 말았다. 여기에 시간을 쏟는 게 시간 낭비처럼 느껴졌기 때문이다.

 최근에 퇴사를 하면서 인수인계 문서를 작성하게 되었다. 문서를 작성하면서, 내가 이 회사에 다니면서 해온 것들을 다시 한번 정리해 볼 수 있는 계기가 되었다. 문득 '이래서 블로그를 하는 건가?'란 생각이 들었다. 그래서 다시 블로그를 시작해 봐야겠다는 생각을 했고, 지금 이 글을 쓰고 있다.

 내가 앞으로 지속 가능한 블로그 운영을 하기 위해서는 지켜야 할 것들이 몇 가지 있다.

 

1. 조급해하지 않기

 예전에 블로그에 글을 남길 때는 빨리, 더 많이 글을 남겨야 한다는 조급한 마음이 있었던 것 같다. 그래야 블로그에 글이 풍성해지고 취업에도 도움이 될 테니까. 의도가 불순했기(?) 때문에 실패했던 걸까? 블로그는 취업에 도움이 되기 때문에 하는 것이 아니라 나 자신의 역량을 키우기 위해 하는 것이다. 일주일에 1개씩 올려도 좋다. 느리지만 꾸준하게.

 

2. 블로그의 글은 '나 자신'을 위한 것이어야 한다.

 아까와 비슷한 맥락이다. 블로그는 취업에 도움이 되기 때문에 하는 것이 아니라, 내가 공부했거나 겪었던 이슈 등에 대해 글로 다시 한번 정리함으로써, 그 내용을 내 머릿속에 각인시키기 위해 하는 것이다. 책을 내는 게 아니므로 지나치게 잘 정리하려고 애쓸 필요는 없다.

 

 나는 앞으로 지속적인 블로깅을 할 수 있을까? 이렇게 글을 써놓고 또 방치될지도 모를 일이다. 그래도 다시 시도해 보려고 한다. 많은 회사에서 중요하게 생각한다는 건, 그만큼 역량을 키우는 데 도움이 되기 때문일 테니까.

반응형

'잡담' 카테고리의 다른 글

면접이란 무엇인가?  (1) 2022.05.12
반응형

1. 불변 컬렉션

 자바 10에서는 불변 컬렉션(unmodifiable collections)와 관련한 몇 가지 변경 사항들이 있다.

 

1.1 copyOf()

 java.util.List, java.util.Map, java.util.Set 각각 copyOf() 라는 새로운 static 메서드가 생겼다. 이는 각 컬렉션의 복제된 불변 컬렉션을 반환한다.

@Test(expected = UnsupportedOperationException.class)
public void whenModifyCopyOfList_thenThrowsException() {
    List<Integer> copyList = List.copyOf(someIntList);
    copyList.add(4);
}

 

1.2 toUnmodifiable*()

 java.util.stream.Collectors는 Stream을 불변 컬렉션으로 collect하기 위한 추가적인 메서드를 List, Map, Set 별로 각각 제공한다.

@Test(expected = UnsupportedOperationException.class)
public void whenModifyToUnmodifiableList_thenThrowsException() {
    List<Integer> evenList = someIntList.stream()
      .filter(i -> i % 2 == 0)
      .collect(Collectors.toUnmodifiableList());
    evenList.add(4);
}

 위의 코드에서처럼 불변 컬렉션에 대해 변경을 시도하면 java.lang.UnsupportedOperationExceptionruntime이 발생할 것이다.

 

2. Optional*.orElseThrow()

 java.util.Optional, java.util.OptionalDouble, java.util.OptionalIntand java.util.OptionalLong 각각 orElseThrow()라는 새로운 메서드가 생겼다(기존에는 Supplier타입의 매개변수를 가진 단항함수만 존재했다). Optional 객체 내에 어떠한 값도 없을 경우, NoSuchElementException을 발생시킨다.

@Test
public void whenListContainsInteger_OrElseThrowReturnsInteger() {
    Integer firstEven = someIntList.stream()
      .filter(i -> i % 2 == 0)
      .findFirst()
      .orElseThrow();
    is(firstEven).equals(Integer.valueOf(2));
}

 이 메서드는 기존의 get() 메서드와 완전히 동일하며, 더 선호되는 방식이다.

 

3. 컨테이너 인식

 JVM은 이제 도커 컨테이너에서 실행되는 것을 인식하고 운영 체제 자체를 쿼리하는 대신 컨테이너별 구성을 추출한다. 이는 컨테이너에 할당된 CPU의 수 및 총 메모리와 같은 데이터에 적용된다. 그러나 이 기능은 Linux 기반 플랫폼에서만 사용할 수 있다. 이 기능은 기본적으로 활성화되어 있으며 JVM 옵션을 사용하여 명령줄에서 사용하지 않도록 설정할 수 있다. 

-XX:-UseContainerSupport

 

 또한 이 변경 사항은 JVM이 사용할 CPU 수를 지정하는 기능을 제공하는 JVM 옵션을 추가한다.

-XX:ActiveProcessorCount=count

 또한 도커 컨테이너 사용자가 Java 힙에 사용될 시스템 메모리의 양을 보다 세밀하게 제어할 수 있도록 세 가지 새로운 JVM 옵션이 추가되었다.

-XX:InitialRAMPercentage
-XX:MaxRAMPercentage
-XX:MinRAMPercentage

 

4. 사용 안 함 및 제거(Deprecations and Removals)

4.1 커멘드 라인 옵션, 툴

 native 메서드를 구현하기 위해 사용되는 C 헤더와 소스 파일을 생성하기 위해 사용되었던 'javah' 기능이 제거되었다. 대신 'javac -h' 를 사용할 수 있다.

 policytool은 정책 파일 생성 및 관리를 위한 UI 기반 도구인데 제거되었다. 사용자는 간단한 텍스트 편집기를 사용하여 이 작업을 수행할 수 있다.

 Java -Xprofoption이 제거되었다. 이 옵션은 실행 중인 프로그램을 프로파일링하고 프로파일링 데이터를 표준 출력으로 전송하는 데 사용되었다. 사용자는 이제 대신 jmap을 사용해야 한다.

 

4.2 APIs

 Deprecated된 java.security.acl 패키지는 @Deprecated(forRemoval = true)로 표시되었으며, 이는 이후 자바 버전에서 제거될 수 있다. 이것은 java.security.Policy 와 관련된 클래스로 대체되었다.

 비슷하게 java.security.{Certificate,Identity,IdentityScope,Signer} 클래스들도 forRemoval = true로 표시되었다.

 

5. 시간 기반 릴리즈 버전 관리

 Oracle은 Java 10을 시작으로 시간 기반 Java 릴리즈로 전환했다. 여기에는 다음과 같은 뜻이 있다.

 

1) 새로운 Java 릴리즈는 6개월마다 제공된다. 2018년 3월 릴리즈는 JDK 10이라면, 9월 릴리즈는 JDK 11가 되는 식이다. 이러한 방식을 기능 릴리즈라고 하며, 하나 또는 두 개의 중요한 기능이 추가되어야 한다. 

2) 기능 릴리즈는 다음 릴리즈가 나오는 6개월 동안만 지원된다.

3) 장기 지원 릴리즈(Long Term Support)는 LTS라고 표시될 것이다. LTS에 대한 지원 기간은 3년이고 Java 11이 LTS 버전이다.

 

 'java -version' 명령어는 이제 GA Date(General Availability Date)를 표시해줄 것이다. 이는 사용자의 JDK가 얼마나 오래되었는지를 쉽게 확인하는 것을 가능하게 해줄 것이다.

$ java -version
openjdk version "10" 2018-03-20
OpenJDK Runtime Environment 18.3 (build 10+46)
OpenJDK 64-Bit Server VM 18.3 (build 10+46, mixed mode)

 

※ 참조

https://www.baeldung.com/java-10-overview

반응형
반응형

1. 개요

 이 글에서는 Java 10 릴리스와 함께 제공되는 성능 향상에 대해 설명한다. 이러한 개선 사항은 JDK 10에서 실행되는 모든 응용 프로그램에 적용되므로, 이를 활용하기 위해 코드를 별도로 변경할 필요는 없다.

 

2. G1 가비지 컬렉터를 이용한 병렬 Full GC(Garbage Collection)

 G1 가비지 컬렉터는 JDK 9부터 디폴트 GC이다. 그러나 G1을 이용한 Full GC는 싱글쓰레드 mark-sweep-compact 알고리즘을 사용했다. Java 10에서부터 병렬 mark-sweep-compact 알고리즘을 사용하도록 변경되었으며, 이는 Full GC에서의 정지시간(stop-the-world time)을 효과적으로 줄여주었다.

 

3. 애플리케이션 클래스 데이터 공유

 JDK 5부터 도입된 클래스 데이터 공유 기능을 통해, 일련의 클래스들은 공유되는 아카이브 파일 안에 사전 처리되어 런타임에 메모리 매핑될 수 있다. 이는 스타트업 시간을 줄일 수 있으며, 여러 JVM이 같은 아카이브 파일을 공유할 경우 동적 메모리 공간을 줄일 수 잇다.

 CDS(Class Data Sharing)은 오직 부트스트랩 클래스 로더에만 허용되다보니, 이 기능은 시스템 클래스에만 제한되었다. AppCDS(Application Class Data Sharing)은 기본 제공 시스템 클래스로더(즉, 앱 클래스 로더), 기본 제공 플랫폼 클래스 로더, 커스텀 클래스 로더가 아카이브 파일을 로드하는 것을 가능하게 했다. 따라서 애플리케이션 클래스에 이 기능을 사용할 수 있다. 

 다음 단계에 따라 이 기능을 사용할 수 있다.

 

3.1 아카이빙할 클래스 목록 가져오기

 다음의 명령어는 HelloWorld 애플리케이션에 의해 로드되는 클래스들을 hello.lst 파일 안에 덤프한다. 

$ java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=hello.lst \ 
    -cp hello.jar HelloWorld

 

3.2 AppCDS 아카이브 파일 생성

 다음의 명령어는 hello.lst를 통해 hello.jsa를 만든다.

$ java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=hello.lst \
    -XX:SharedArchiveFile=hello.jsa -cp hello.jar

 

3.3 AppCDS 아카이브 파일 사용

 다음의 명령어는 hello.jsa와 함께 HelloWorld 애플리케이션을 실행한다.

$ java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=hello.jsa \
    -cp hello.jar HelloWorld

 AppCDS는 오라클의 JDK 8 및 JDK 9용 상용 기능이었으나, 지금은 오픈 소스이며 공개적으로 사용할 수 있다.

 

4. 실험적인 자바 기반의 JIT(Just In Time) 컴파일러

 Graal은 HotSpot JVM과 통합된 Java로 작성된 동적 컴파일러로, 고성능 및 확장성에 중점을 두고 있다. 이것은 또한 JDK 9에 도입된 실험적인 AOT(Ahead Of Time) 컴파일러의 기초이기도 하다.

 JDK 10은 Graal 컴파일러를 리눅스/x64 플랫폼에서 실험적인 JIT 컴파일러로 사용할 수 있게 한다. Graal을 다음 명령어를 통해 JIT 컴파일러로 사용 가능하다.

-XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler

 이것은 실험적인 기능이며, 현존하는 JIT 컴파일러보다 항상 더 나은 성능을 보장하지는 않는다는 것을 주의해야한다.

 

 

* 추가적인 공부가 필요해보이는 항목들 (2021/09/27)

- G1 가비지 컬렉터란 무엇인가? 가비지 컬렉팅 프로세스에 대한 이해. mark-sweep-compact 알고리즘? Full GC

- 시스템 클래스 로더와 플랫폼 클래스 로더의 차이는?

- 현존하는 JIT 컴파일러는 어떻게 사용하는 걸까? 이미 JDK에 적용이 되어있는 건가?

 

 

※ 참조

https://www.baeldung.com/java-10-performance-improvements

반응형
반응형

1. 지역변수 타입 추론

 Java 10에서 가장 눈에 띄는 변화 중 하나는 초기화를 통한 지역변수 타입 추론이다. 자바 9까지는 지역 변수의 타입을 명시해야 했으며, 그것이 오른쪽에 대입되는 객체와 호환이 가능한지를 확인해야 했다.

String message = "Good bye, Java 9";

 자바 10부터는 다음과 같이 지역변수를 선언할 수 있다.

@Test
public void whenVarInitWithString_thenGetStringTypeVar() {
    var message = "Hello, Java 10";
    assertTrue(message instanceof String);
}

 위에서는 message에 타입을 명시하지 않는다. 대신 var로 표시하고, 컴파일러는 오른쪽에 대입되는 객체를 통해 message의 타입을 추론한다. 위의 예시에서 message의 타입은 String일 것이다. 

 

 이 기능은 초기화가 되는 지역 변수에만 사용할 수 있다. 멤버 변수, 메서드 매개 변수, 반환형 등에 사용할 수 없으며, 컴파일러가 타입을 추론할 수 있게 하기 위해 초기화가 필요하다.

 이 기능은 보일러플레이트 코드를 줄이는데 도움을 준다.

Map<Integer, String> map = new HashMap<>(); // Before Java 10
var idToNameMap = new HashMap<Integer, String>(); // Java 10

 이것은 변수의 타입보다 변수명에 더 집중하는데도 도움을 준다.

 

 주의할 점 중 하나는, var는 키워드가 아니라는 것이다. 이는 'var'를 변수명이나 함수명 등에 이미 사용하여 구현된 프로그램들에 대한 하위 호환성을 보장해준다. var는 int와 마찬가지로 예약어이다.

 또한 var을 사용한다고 해서 런타임에 오버헤드가 발생하지 않으며, 자바가 동적 타입 언어가 된 것도 아니라는 것을 기억해야 한다. 변수의 타입은 컴파일 타임에 추론되며, 나중에 바뀔 수 없다.

 

2. var의 잘못된 사용

 이미 언급했듯이, var은 초기화되지 않는 지역 변수에는 사용될 수 없다.

var n; // error: cannot use 'var' on variable without initializer

 

null로 초기화할 수도 없다. null을 통한 타입 추론이 불가능하기 때문이다.

var emptyList = null; // error: variable initializer is 'null'

 

 

 var은 지역변수에만 사용 가능하다. 멤버 변수 등에는 사용이 불가능하다.

class Hello {
    public var = "hello"; // error: 'var' is not allowed here
}

 

 람다 식의 경우, 명시적인 타겟 타입이 필요하다. 따라서 var 사용이 불가능하다.

var p = (String s) -> s.length() > 10; // error: lambda expression needs an explicit target-type

 

 아래와 같이 배열을 사용하는 경우에도 사용할 수 없다.

var arr = { 1, 2, 3 }; // error: array initializer needs an explicit target-type

 

3. var 사용 가이드라인

 var를 사용하는 것이 가능은 하지만, 사용하는 것이 별로 좋은 선택이 아닌 경우가 있다. 예를 들면 가독성을 떨어뜨리는 경우다.

var result = obj.prcoess();

 

 위에서는 process() 메서드에 의해 반환되는 객체의 타입을 이해하는 것이 쉽지 않으므로 가독성이 떨어진다. 

 우리가 이 기능을 어떻게 사용해야할 지에 대한 가이드라인이 있다. 여기를 클릭하면 확인할 수 있다. 

 

 긴 파이프라인을 가진 스트림을 사용할 때도 var 사용을 피해야 한다. 

var x = emp.getProjects.stream()
  .findFirst()
  .map(String::length)
  .orElse(0);

 

 또한 var 사용은 예상하지 못하는 결과를 가져올 수 있다.

 예를 들면, 아래와 같이 다이아몬드 연산자를 써보자.

var empList = new ArrayList<>();

 empList의 타입은 List<Object>가 아닌 ArrayList<Object>가 될 것이다. 만약 ArrayList<Employee>로 명시하고 싶다면 아래와 같이 하면 된다.

var empList = new ArrayList<Employee>();

 

 또한 다음과 같은 경우에도 예상치 못한 에러가 발생할 수 있다.

@Test
public void whenVarInitWithAnonymous_thenGetAnonymousType() {
    var obj = new Object() {};
    assertFalse(obj.getClass().equals(Object.class)); // false
    obj = new Object() // error!!
}

  obj에 new Object()를 대입할 수 없다. 왜냐하면 obj의 타입은 Object가 아니라 Object를 확장한 익명클래스이기 때문이다.

 

※ 참조

https://www.baeldung.com/java-10-local-variable-type-inference

반응형

+ Recent posts