Java 10 에서 var 재대로 사용하기
Tags: java, var, korean
Published: 2020-03-24 07:02:39

조금 된 일이지만, Java 10부터 `var` 구문이 생겼다. LTS인 자바 11부터는 이를 통한 람다 타입 지원도 생겼다.

하지만 최신 안정화 버전(LTS)인 자바 11로 프로젝트를 시작한들, `var` 구문에 대한 거부감을 가진 개발자들이 많을 것이다.
왜냐하면 javascript 의 `var` 구문에 대한 오해 때문이다.
javascript 는 아무래도 타입 종속적인 변수를 가진 언어가 아니기 때문에 javascript 에 불만을 가지고 있는 개발자는 한국에서도 많고 외국에서도 많다. 괜히 자바스크립트를 강타입으로 관리하는 타입스크립트가 인기 있는 것이 아니다.
당장 유명한 C#의 경우, 닷넷 3.5 기준으로, C# 3.0 스팩부터 이미 추가되었는데, 생긴 지 2007년, 즉, 13년이나 된 오래된 기술이고, 타입 추론 개념도 이것보다 더 오래된 기술이다.
자바가 늦게 수용했다고 해도 과언이 아닌데, 자바만 해온 개발자들에게는 javascript 인식이 박힌 `var` 구문이 달갑지 않을 것이다.

하지만 잊지 말자. `var` 가 추가한들, 초기부터 강타입으로 설계된 자바는 절대 변하지 않았다. 그리고 앞으로도 그럴 것이다. 강타입 철학이 깨지면 많은 자바 커뮤니티의 막강한 반발에 부딪힐 거 뻔하고, 자바를 위해 커뮤니티의 수용을 해야 하는 오라클은 그들을 무시할 수 없기 때문이다.

일단 컴파일 이론에서 '타입 추론'에 대한 개념은 직접 찾아보라. 여기서 얘기할 게 아니고, 얘기하자면 길어서 글 쓰기 지친다.

사실 짧게 설명하겠지만 자바가 왜 이렇게 타입 추론 개념을 이제서야 도입할 정도의 보수적인 언어냐, 간단하다. **바로 하위 호환성.**
자바 개발진들 생각보다 상당히 보수적이며, 하위 호환성 보장을 최우선으로 중요하게 생각한다. 그렇다 보니 제네릭만 봐도 참 미치게 할 정도의 이상한 스펙이 생기기 마련. 자바 개발 커뮤니티도, 자바 개발자도 이에 따라 보수적이기도 하다.
폴리글랏에 관심있는 개발자라면, 이런 답답한 자바가 이렇게 싫을 수 없을 것이다.

자, 의미없는 서론은 여기까지 하고, 타입을 컴파일러가 추론할 `var` 구문을 어떻게 하면 의미있게 사용할 수 있을까? 내가 제시하겠다. 난 C#도 많이 해봤고 하다 보니 다른 닷넷 개발자들도 `var` 에 대한 사용법을 잘 알고 있는데, 자바는 도입한지 얼마 안되다 보니 어떻게 사용하는게 좋을 지 해맬 것이다. 지금부터 따라하면 당신의 자바는 모던 자바가 되는 것이다.

## `foreach`

그냥 우리가 리스트 열거할 때 쓰던 `for`문, `for(Person person : personList)` 문에서 변수 선언할 때 `var`로 쓰면 편하다. 이클립스나 인텔리제이나 `for`문 쓸때, 타입 추론하기엔 열거 타입을 정의할 때까지 타입을 직접 작성하거나, 템플릿을 작성해야 해서 난감할 때를 많이 겪어봤을 것이다. 이를 `var` 키워드가 순식간에 해결해준다.

```java
for (var person : personList) {
    // ...
}
```

이렇게 작성하면 IDE 에서는 `var` 키워드를 `Person` 클래스로 인식할 수 있는 기회가 주어지게 되고, 컴파일할 때도 `var` 키워드를 `Person` 으로 변환하게 될 것이다. 타이핑이 정말 간결해진다. `Object` 타입으로 미리 단정지을 필요도 없다.

## 람다 (Java 11)

LTS인 자바 11은 람다 인자에도 `var`를 넣게 해주는데, 이게 왜 중요하냐면,
일반 람다의 경우 파라미터 어노테이션을 못 집어넣는다. 만약 어노테이션을 넣고 싶으면 따로 메소드로 빼던가, 익명 클래스로 정의해야 했었다.
하지만 자바 8부터 람다 인자는 타입 추론의 기초였던 게, 자바 11부터는 타입 추론의 유연성을 추가했다. 즉, 이렇게 가능해진다.

```java
Consumer<Person> personConsumer = (@Nonnull var person) -> {
    // @Nonnull 어노테이션에 의해 person에 널체크부터 하겠지?
}
```

어노테이션 프로세싱과 어노테이션의 장점만 생각해도 이건 유연성이 증대되는 효과가 있다.

## 익명 클래스

일단 보통 가독성 때문에 대부분 개발자들은 아래와 같은 심플한 `var` 사용을 지양하려고 한다. 왜냐면 IDE라면 모를까 임시로 자동완성을 지원하지 않는 에디터로 편집할 때 사람이 타입 추론이 어려워서이다.

```java
var intVal = 20;
var strVal = "string";
var list = new ArrayList<Integer>();
```

하지만 익명 클래스가 출동하면 어떨까?

```java
var supply = new Supplier<String>() {
    @Override
    public String get() {
        // 뭐 할짓거리
    }
}
```

익명 클래스는 정의가 거대하고, 유추가 쉽다. 그리고 선언한 다음에 변수가 바뀌는 일도 없다.
하지만 람다는 안 된다. 하면 안 된다. 어자피 해봐야 컴파일 오류 난다.

```java
var supply = () => {
    // 뭐 할짓거리 해봐야 컴파일 오류
};
```

왜냐면 람다를 등록할 인터페이스가 모호하기 때문이다. 같은 람다라도 예를 들어 `Supplier<T>` 클래스가 있고 `Callable<T>` 클래스가 있다. 물론 미묘한 차이점이라면 `throws Exception` 유무지만, 만약 `Supplier<T>` 와 같은 람다를 가진 인터페이스가 있다면 무슨 수로 유추할 것인가? 컴파일러는 그럴 능력이 없다. 그리고 그걸 고려해서 설계할 생각도 없다. 기준이 없기 때문에 모호하다.

생각해 보면 `var` 쓸 곳이 그리 많지 않아 보인다.
하지만 위 케이스에 쓰기만 해도 `var` 키워드가 존재하는 이유, 타입 추론이 필요한 이유를 알 수 있다.

C#의 경우 `var` 키워드의 미친 존재감이 2가지 있는데, 하나는 익명 클래스다. 자바의 익명 클래스와는 개념이 다른데, 개발자가 직접 속성달린 읽기 전용 레코드를 정의하는 방식이다.

```cs
var record = new {
    Name = "홍길동",
    Age = 42
}
```

C# 에서는 저걸 컴파일할 때 클래스를 만들고, 해당 속성에 들어간 제네릭을 생성하여 선언하기 때문에 속성 타입 유추가 쉽다. C#은 `List` 클래스와 `List<T>` 클래스가 따로 있으며, 다른 클래스 취급한다. 자바에서는 `List` 하나 뿐이고 `List<T>` 로 정의되어 있다. 하위 호환성의 폐해지.

그리고 하나는 바로 그 유명한 Linq 이다. 뭐낙 유명하여 따로 설명하지는 않겠지만, Linq의 끝은 대부분 익명 클래스, 엄밀히 말하면 묵시적으로 생성된 인터페이스가 주류이기 때문에 `var` 사용과 미사용 차이가 크다.

그래서 닷넷 개발자에게 `var` 는 오히려 환영할 키워드다.
이제 `var`를 환영할 때를 맞이하라. 내가 가이드준 대로만 써도 환영할 수 있을 것이다.

끗.