이제부터는 최신 Java 문법(Java 17+) 시리즈를 시작한다.
첫 번째는 var 키워드이다.
이 글은 단순히 "타입을 생략하는 문법"이 아니라 Java가 왜 var를 도입했고, 언제 사용해야 하는지를 실무 관점에서 설명한다.
var 키워드
var는 새로운 타입이 아니라 컴파일러가 지역 변수(Local Variable)의 타입을 자동으로 추론(Type Inference)하는 문법이다.
(Java 10부터 지원)
Java는 왜 var를 추가했을까?
Java는 오랫동안 이런 코드를 사용했다.
Map<String, List<User>> userMap =
new HashMap<String, List<User>>();
변수 이름보다 타입이 더 길다.
Stream를 사용하면 더 심해진다.
Map<String, Long> result =
users.stream()
.collect(
Collectors.groupingBy(
User::getDepartment,
Collectors.counting()
)
);
실제로 개발자는
변수 이름보다 Generic 타입을 읽는 시간이 더 길어졌다.
그래서 Java는
컴파일러가 타입을 알 수 있다면 대신 써주자.
라는 개념으로 var를 도입했다.
첫 번째 오해
많은 사람들이
var name = "Kim";
을 보고
Java도 JavaScript처럼 동적 타입 언어가 된 건가?
라고 생각한다.
절대 아니다.
Java는 여전히 정적 타입 언어이다.
var name = "Kim";
컴파일러는 내부적으로
String name = "Kim";
으로 변환한다.
즉,
작성
var name = "Kim";
↓
컴파일
String name = "Kim";
↓
실행
이다.
타입은 절대로 변하지 않는다.
var name = "Kim";
name = "Lee";
가능하다.
하지만
var name = "Kim";
name = 100;
는
컴파일 오류이다.
이미
String name = "Kim";
으로 결정되었기 때문이다.
var는 Object도 아니다.
다음 코드를 보자.
var list = List.of("A", "B");
많은 사람들이
Object list = List.of("A", "B");
라고 생각한다.
실제로는
List<String> list = List.of("A", "B");
이다.
그래서
list.add(100);
는
컴파일 단계에서 바로 오류가 발생한다.
var는 어디에서 사용할 수 있을까?
1. 지역 변수
public void save() {
var name = "Kim";
}
가능하다.
2. for문
for (var i = 0; i < 10; i++) {
}
가능하다.
3. 향상된 for문
for (var user : users) {
}
가능하다.
사용할 수 없는 곳
필드
class User {
var name = "Kim";
}
컴파일 오류
메서드 파라미터
public void save(var name) {
}
컴파일 오류
반환 타입
public var findUser() {
}
컴파일 오류
null
var value = null;
컴파일 오류
왜냐하면
null
↓
타입 정보 없음
↓
컴파일러가 추론 불가능
이기 때문이다.
Generic과 함께 사용할 때 가장 빛난다.
기존
HashMap<String, List<User>> userMap =
new HashMap<>();
var 사용
var userMap =
new HashMap<String, List<User>>();
읽기가 훨씬 편하다.
Stream에서도 많이 사용한다.
기존
List<User> activeUsers =
users.stream()
.filter(User::isActive)
.toList();
var
var activeUsers =
users.stream()
.filter(User::isActive)
.toList();
코드가 훨씬 간결해진다.
Builder Pattern과도 잘 어울린다.
기존
User user =
User.builder()
.name("Kim")
.age(30)
.build();
var
var user =
User.builder()
.name("Kim")
.age(30)
.build();
Builder의 반환 타입이 변경되어도 선언부를 수정할 필요가 없다.
하지만 남용하면 안 된다.
다음 코드를 보자.
var result = service.execute();
여기서
result
↓
User?
Order?
String?
Map?
List?
전혀 알 수 없다.
반대로
var userMap =
new HashMap<Long, User>();
은
변수명
+
초기값
만 봐도 타입이 명확하다.
Oracle이 권장하는 사용법
Oracle도 var 사용 시
좋은 변수명과 함께 사용할 것
을 권장한다.
좋은 예
var activeUsers =
userRepository.findAll();
좋지 않은 예
var data =
service.execute();
또는
var x = ...
var y = ...
var z = ...
실무에서 가장 많이 사용하는 패턴
Collection 생성
var userMap =
new HashMap<Long, User>();
Stream 결과
var result =
users.stream()
.collect(Collectors.toList());
Optional
var optionalUser =
repository.findById(id);
Builder
var order =
Order.builder()
.id(1L)
.build();
var를 사용할지 말지 판단하는 기준
좋은 경우
var users =
userRepository.findAll();
- 타입이 명확하다.
- 코드가 짧아진다.
- 가독성이 좋아진다.
좋지 않은 경우
var value =
service.execute();
- 타입이 보이지 않는다.
- IDE 없이는 이해하기 어렵다.
- 코드 리뷰가 어려워진다.
실무 스타일
대부분의 실무 프로젝트에서는 다음 원칙을 많이 사용한다.
적극적으로 사용
var userMap = new HashMap<String, User>();
var users = userRepository.findAll();
var order = Order.builder().build();
명시적으로 타입 작성
User user = service.findUser();
OrderResponse response = service.save();
List<Product> products = repository.findAll();
반환 타입이 비즈니스 의미를 가지는 경우에는 명시적으로 적는 경우가 많다.
면접 포인트
var란?
새로운 타입이 아니라 컴파일러가 지역 변수의 타입을 자동으로 추론하는 문법이다.
var는 동적 타입인가?
아니다.
var name = "Kim";
은 컴파일 시
String name = "Kim";
으로 결정되며 이후 타입은 변경되지 않는다.
var의 장점
- 코드 간결
- Generic 중복 제거
- Builder, Stream과 높은 궁합
- 리팩토링에 유리
var의 단점
- 타입이 숨겨질 수 있다.
- 의미 없는 변수명과 함께 사용하면 가독성이 떨어진다.
- 필드, 파라미터, 반환 타입에는 사용할 수 없다.
다음 글
다음 글은 Text Block(Java 15) 이다.
- """ 문법이 등장한 이유
- SQL, JSON, HTML 문자열을 왜 더 읽기 쉽게 만들었는지
- 들여쓰기(stripIndent) 규칙
- 실무에서 MyBatis, JPA Native Query, JSON 작성 시 어떻게 활용하는지
를 실무 예제와 함께 자세히 설명하겠다.
'language > java' 카테고리의 다른 글
| Switch Expression(Java 14) (0) | 2026.06.17 |
|---|---|
| Text Block(Java 15) (0) | 2026.06.17 |
| Reflection + Annotation + Proxy + ThreadLocal (0) | 2026.06.16 |
| ThreadLocal Memory Leak (0) | 2026.06.16 |
| ThreadLocal 원리 (0) | 2026.06.16 |
댓글