본문 바로가기

기본 타입과 리터럴

값의 범위와 연산에 맞는 타입·리터럴을 선택하고 컴파일러가 허용하는 이름과 읽기 좋은 이름을 구분합니다.

변수 이름이 의미를 설명한다면 타입은 그 값에 허용되는 범위와 연산을 제한합니다.

회원가입의 나이는 정수이고, 회원 식별자는 int 범위를 넘을 수 있으며, 활성 여부는 0과 1보다 boolean이 정확합니다.

리터럴은 소스에 직접 적은 값이므로 접미사와 따옴표까지 타입의 일부로 읽어야 합니다.


정수 리터럴의 범위

다음 프로그램은 값이 30억인 회원 식별자를 long 변수에 넣으려 하지만 오른쪽 리터럴 자체가 기본 int 범위를 넘습니다.

lab/TooLargeLiteral.java
public final class TooLargeLiteral {
    public static void main(String[] args) {
        long memberId = 3_000_000_000;
        System.out.println(memberId);
    }
}
lab\TooLargeLiteral.java:3: error: integer number too large
        long memberId = 3_000_000_000;
                            ^

왼쪽이 long이어도 오른쪽 정수 리터럴은 접미사가 없으면 먼저 int로 해석됩니다.

값 뒤에 대문자 L을 붙여 리터럴 자체를 long으로 만듭니다.

long memberId = 3_000_000_000L;

소문자 l도 문법상 가능하지만 숫자 1과 헷갈리므로 대문자 L을 사용합니다.

밑줄은 숫자 값을 바꾸지 않고 자릿수만 읽기 쉽게 나눕니다.

3_000_000_000L과 3000000000L은 같은 값입니다.

문자열 리터럴도 숫자로 자동 변하지 않습니다.

lab/StringIsNotInt.java
public final class StringIsNotInt {
    public static void main(String[] args) {
        int age = "45";
        System.out.println(age);
    }
}
error: incompatible types: String cannot be converted to int

"45"는 숫자처럼 보이는 두 문자로 이루어진 문자열입니다.

계산하려면 Integer.parseInt("45")처럼 변환 성공과 실패가 있는 별도 경계를 통과해야 합니다.

Java 컴파일러가 리터럴 표기와 범위를 먼저 판정하고 초기 타입을 확정한 뒤 목적지 타입과의 호환성을 검사하는 흐름입니다. L 없는 큰 정수와 String에서 int로의 대입은 서로 다른 경계에서 실패합니다.

JAVA · COMPILE FLOW

리터럴의 표기가 대입 전에 타입을 결정한다

왼쪽 변수보다 먼저 오른쪽 리터럴의 표기·범위·초기 타입을 판정해야 대입 성공과 실패를 설명할 수 있습니다.

리터럴의 표기가 대입 전에 타입을 결정한다 L 없는 큰 정수는 리터럴 단계 오류; L 있는 long은 대입 가능; String→int 직접대입과 명시파싱의 별도 경계 정수 리터럴 판정 뒤 목적지 타입 검사 숫자 문자열의 별도 변환 L 없음 L 있음 큰 정수 리터럴 3_000_000_000 접미사 L이 있는가? 리터럴 단계 컴파일 오류 int 범위를 넘는 L 없는 표기 long 값 → long 변수 3_000_000_000L 대입 가능 직접 대입 명시 파싱 String 값 "45" 유효한 값이지만 int는 아님 직접 대입 오류 String ≠ int parseInt("45") 명시 파싱 형식·범위 전제 int 값 45 age에 대입 가능
리터럴의 표기가 대입 전에 타입을 결정한다 L 없는 큰 정수는 리터럴 단계 오류; L 있는 long은 대입 가능; String→int 직접대입과 명시파싱의 별도 경계. 본문과 바로 아래 설명이 원래 코드·수치를 보완합니다. 정수 리터럴 → 대입 String → int의 별도 경계 L 없음 L 있음 직접 대입 명시 파싱 큰 정수 리터럴 3_000_000_000 접미사 L이 있는가? int 범위 초과 리터럴 단계에서 컴파일 오류 long 값 생성 3_000_000_000L long 변수에 대입 가능 String 값 "45" 값 자체는 유효하지만 int 값은 아님 직접 대입 오류 String ≠ int parseInt("45") 명시적으로 정수 파싱 숫자 형식·범위 전제 int 값 45 age에 대입 가능
  1. 먼저 오른쪽 리터럴의 타입과 범위를 확인합니다

    L 없음 · 리터럴 단계에서 실패

    long memberId = 3_000_000_000;

    왼쪽은 long이어도, L 없는 십진 정수 리터럴이 int 범위를 넘습니다.

    컴파일 오류: integer number too large

    L 있음 · long 값을 만듦

    long memberId = 3_000_000_000L;

    접미사 L이 리터럴 자체를 long으로 해석하게 하므로 대입할 값을 만들 수 있습니다.

    리터럴 타입: long

  2. 그 값이 왼쪽 변수 타입에 대입 가능한지 확인합니다

    문자열을 정수로 바로 대입 · 실패

    int age = "45";

    "45"는 유효한 String 리터럴이지만 int로 자동 변환되지 않습니다.

    컴파일 오류: String → int 대입 불가

    명시적으로 파싱 · 별도 변환 경계

    int age = Integer.parseInt("45");

    이 예제는 정수 45를 만듭니다. 다른 문자열은 형식이나 범위에 따라 변환에 실패할 수 있습니다.

표기도 타입의 일부입니다

45 → int · 0.75 → double · true → boolean · 'A' → char · "45" → String

타입은 목적지 변수의 장식이 아닙니다. 소스 표현이 먼저 값을 만들고, 그 결과가 목적지 계약을 통과해야 합니다.


기본 타입과 값의 대응

src/MemberTypes.java
public final class MemberTypes {
    public static void main(String[] args) {
        int age = 45;
        long memberId = 3_000_000_000L;
        double signupRate = 0.75;
        boolean active = false;
        char grade = 'A';
        String email = "[email protected]";

        System.out.println("age=" + age);
        System.out.println("memberId=" + memberId);
        System.out.println("signupRate=" + signupRate);
        System.out.println("active=" + active);
        System.out.println("grade=" + grade);
        System.out.println("email=" + email);
    }
}
age=45
memberId=3000000000
signupRate=0.75
active=false
grade=A
[email protected]

정수형은 byte, short, int, long이 있지만 일반적인 정수 계산은 int, 큰 누적값은 long을 우선합니다.

파일의 원시 바이트처럼 API가 byte를 요구할 때는 그 타입을 사용합니다.

작은 값을 담는다고 무조건 작은 타입을 택하면 변환과 오버플로 검토가 늘어날 수 있습니다.

실수형은 float와 double이 있습니다.

접미사 없는 0.75는 double이고 float 리터럴은 0.75F처럼 표시합니다.

일반 계산은 double을 사용하지만 금액처럼 정확한 십진 연산이 필요한 경우에는 부동소수점 자체의 오차를 고려해 뒤에서 BigDecimal 같은 타입을 선택합니다.

boolean은 true와 false만 가집니다.

int active = 2처럼 유효하지 않은 세 번째 상태를 만들지 않습니다.

'A'나 '가'처럼 작은따옴표로 쓰는 값은 char, 큰따옴표로 쓰는 문자열은 String입니다.

정확히는 char 하나가 UTF-16의 16비트 코드 단위 하나를 담습니다. 화면에 보이는 문자 하나가 언제나 char 하나인 것은 아닙니다. 예를 들어 "😀"는 String으로 표현하고 UTF-16 코드 단위 두 개를 사용합니다. 이 수업의 등급 'A'는 한 단위로 표현되므로 char를 써도 됩니다. 문자와 코드 단위의 차이는 문자열 수업에서 다시 다룹니다. Java 언어 명세의 기본 타입 정의도 이 구분을 사용합니다.

실제 업무의 제목과 상태 라벨은 길이가 변하므로 대부분 String이 자연스럽습니다.


리터럴과 타입

소스 표현리터럴 타입회원가입에서의 의미
45int회원 나이
3_000_000_000Llong큰 회원 식별자
0.75double가입 전환율
trueboolean활성 여부
'A'char문자 하나인 등급
"[email protected]"String회원 이메일

리터럴은 고정된 소스 표현이고 변수의 현재 값은 대입으로 바뀔 수 있습니다.

int age = 45; age = 60;에서 45와 60은 각각 리터럴이며 age의 값만 시간에 따라 달라집니다.

숫자 타입의 범위를 암기하기보다 경계를 출력해 확인할 수 있습니다.

src/NumericRanges.java
public final class NumericRanges {
    public static void main(String[] args) {
        System.out.println("byte=" + Byte.MIN_VALUE + ".." + Byte.MAX_VALUE);
        System.out.println("short=" + Short.MIN_VALUE + ".." + Short.MAX_VALUE);
        System.out.println("int=" + Integer.MIN_VALUE + ".." + Integer.MAX_VALUE);
        System.out.println("long=" + Long.MIN_VALUE + ".." + Long.MAX_VALUE);
        System.out.println("float.precision=" + Float.PRECISION);
        System.out.println("double.precision=" + Double.PRECISION);
    }
}

Float.PRECISION과 Double.PRECISION은 이진 부동소수점 가수의 비트 정밀도를 보여 줍니다.

표현 가능한 최대 크기와 정확히 보존되는 십진 자릿수는 같은 개념이 아닙니다.


식별자 문법과 관례

다음 선언은 컴파일되지 않습니다.

lab/IllegalNames.java
public final class IllegalNames {
    public static void main(String[] args) {
        int 2age = 45;
        int class = 1;
    }
}

식별자는 숫자로 시작할 수 없고 class 같은 예약어를 변수 이름으로 쓸 수 없습니다.

문자, 숫자, 밑줄, 달러 기호를 조합할 수 있지만 숫자는 첫 글자가 될 수 없습니다.

Java는 대소문자를 구분하므로 age와 Age는 다른 변수지만 이런 차이로 의미를 구분하면 읽기 어렵습니다.

문법상 가능한 다음 이름도 피합니다.

int $ = 45;
int _age = 50;
int a = 60;

일반 변수와 메서드는 memberAge처럼 소문자로 시작하는 로어 카멜 케이스를 사용합니다.

클래스는 MemberTypes처럼 대문자로 시작하고, 변경되지 않는 상수는 뒤에서 MAX_AGE처럼 대문자와 밑줄을 사용합니다.

m보다 age, flag보다 active가 값의 단위와 의미를 직접 알려 줍니다.


회원 정보의 타입 설계

회원가입 결과에 나이와 활성 여부를 추가합니다.

이 예제에서는 나이·식별자 인자가 각각 int·long 범위 안의 정수 문자열이며 나이는 1세 이상이라고 가정합니다. 빠진 인자는 기본값으로 채웁니다. 숫자가 아닌 인자의 실패 처리는 다음 장에서 배웁니다.

처음 보는 표현은 다음 뜻으로 읽으면 됩니다.

  • 조건 ? 왼쪽 값 : 오른쪽 값은 조건이 참이면 왼쪽, 거짓이면 오른쪽 값 하나를 고릅니다. 첫 줄은 인자가 있으면 그 값을, 없으면 기본 이메일을 고릅니다.
  • Integer.parseInt와 Long.parseLong은 문자열을 각각 int, long 값으로 바꿉니다. "45"와 숫자 45를 연결하는 단계입니다.
  • (double) age는 나눗셈 전에 나이를 실수 계산에 참여시키고, age >= minimumAge는 나이가 기준 이상인지 boolean으로 만듭니다.

조건 선택은 switch 식과 삼항 연산, 변환 위치는 스코프와 숫자 형변환에서 직접 연습합니다. 여기서는 각 결과가 어느 타입에 담기는지 먼저 따라가세요.

src/MemberPreview.java
public final class MemberPreview {
    public static void main(String[] args) {
        String email = args.length > 0 ? args[0] : "[email protected]";
        int age = args.length > 1 ? Integer.parseInt(args[1]) : 45;
        long memberId = args.length > 2 ? Long.parseLong(args[2]) : 3_000_000_000L;
        int minimumAge = 18;
        double ageRatio = (double) age / minimumAge;
        boolean eligible = age >= minimumAge;

        System.out.println("email=" + email);
        System.out.println("age=" + age);
        System.out.println("memberId=" + memberId);
        System.out.println("ageRatio=" + ageRatio);
        System.out.println("eligible=" + eligible);
    }
}
java -cp out MemberPreview [email protected] 45 3000000000
[email protected]
age=45
memberId=3000000000
ageRatio=2.5
eligible=true

3_000_000_000L의 L은 리터럴 자체를 long으로 해석하게 합니다.

문자열 인자를 읽을 때도 Integer.parseInt와 Long.parseLong을 구분해야 합니다.

계산 타입은 마지막 저장 타입만 보고 정해지지 않습니다.


연습 문제

MemberValueCatalog를 작성해 다음 값을 적절한 타입과 이름으로 선언하고 출력하세요.

  • 회원 나이 90세
  • 회원 식별자 9,000,000,000
  • 가입 전환율 82.5%
  • 활성 여부
  • 등급 문자 B
  • 이메일 [email protected]

큰 정수 리터럴과 변수 이름의 단위가 코드에 드러나야 합니다.

해설 보기
src/MemberValueCatalog.java
public final class MemberValueCatalog {
    public static void main(String[] args) {
        int age = 90;
        long memberId = 9_000_000_000L;
        double signupRate = 82.5;
        boolean active = true;
        char grade = 'B';
        String email = "[email protected]";

        System.out.println("age=" + age);
        System.out.println("memberId=" + memberId);
        System.out.println("signupRate=" + signupRate);
        System.out.println("active=" + active);
        System.out.println("grade=" + grade);
        System.out.println("email=" + email);
    }
}

위 해설은 문제에서 정한 퍼센트 값 82.5를 사용하지만, signupRate와 출력 라벨만으로는 퍼센트인지 0부터 1 사이 비율인지 알 수 없습니다. 단위를 코드에 드러내라는 조건까지 충족하려면 해당 변수를 signupPercent로 바꾸고 출력에도 %를 붙이세요.

double signupPercent = 82.5;
System.out.println("signupPercent=" + signupPercent + "%");

이 출력은 signupPercent=82.5%입니다. 숫자만 맞는 답과 단위까지 분명한 답을 구분해야 합니다.

0.825 비율을 선택한다면 이름을 signupRatio로 바꾸어 단위를 드러냅니다.