Java 10 에서 var 재대로 사용하기

Composite·2020년 5월 27일
3

조금 된 일이지만, 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 키워드가 순식간에 해결해준다.

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

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

람다 (Java 11)

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

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

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

익명 클래스

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

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

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

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

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

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

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

추가: 복잡한 제네릭

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

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

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

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

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

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

끗.

profile
지옥에서 온 개발자

0개의 댓글