콘텐츠로 이동

프로그램 구조와 타입

Unibase는 .uni 소스에 클래스와 메서드를 작성하는 언어입니다. 파일 안에는 필요한 타입을 가져오는 선언, 타입 정의, 값을 처리하는 문장이 들어갑니다. 아래 문법 형태는 선언을 읽기 위한 조각이며, 실행 가능한 전체 소스는 연결된 본문에서 제공합니다.

소스는 UTF-8로 저장합니다. 일반적인 선언 순서는 package, import, 클래스·인터페이스입니다. 패키지는 타입의 이름 공간을 만들고, import는 다른 패키지의 타입을 짧은 이름으로 사용할 때 씁니다.

선언의 배치
package sample;
import unibase.system.Console;
class Example {
static void main() {
// 실행할 문장
}
}

sample.Example은 패키지를 포함한 이름이고 Example은 단순 이름입니다. package sample.tools;처럼 점으로 하위 이름 공간을 표현할 수 있습니다. 패키지 이름이 폴더·빌드 설정을 자동으로 만드는 것은 아닙니다.

import unibase.collections.List;
import unibase.concurrent.*;

첫 형태는 사용할 타입 하나를, 두 번째 형태는 패키지의 타입을 가져오는 선언입니다. 실제 사용 타입이 드러나도록 예제에서는 명시적인 import를 우선합니다. 컴파일러는 일부 기본 패키지를 자동으로 읽지만, 이를 모든 패키지의 자동 import로 일반화하지 않습니다. use는 import의 대체 문법이 아닙니다.

class의 중괄호에는 필드·생성자·메서드를 둡니다. 메서드의 중괄호에는 지역 변수·대입·호출·분기·반복·반환 등의 문장을 둡니다. 문장 끝에 세미콜론을 사용합니다.

static void main()은 콘솔 프로그램의 시작점입니다. static은 객체를 만들지 않는 클래스 수준 메서드, void는 반환값 없음, main은 시작점 이름, ()는 이 형태에서 매개변수 없음을 나타냅니다. 시작점에는 public을 생략할 수 있습니다.

첫 소스의 main()에서 출력 문장을 찾은 뒤, 메서드의 매개변수·반환값을 비교해 보세요. 실행용 일반 소스 검사와 시작점이 없는 라이브러리 검사는 서로 다른 용도입니다.

타입 표현하는 값 선언을 읽는 예
byte, short, int, long 정수 타입 int count = 3;
float, double 실수 타입 double ratio = 0.5;
bool 참과 거짓 bool ready = true;
char 문자 타입 문자열 타입과 구분
string 문자열 string label = "Unibase";
클래스 타입 해당 객체 Counter counter = Counter(0);
제네릭 타입 타입 인자를 지정한 값 List<int> values = [1, 2];

타입의 모든 비트 폭·숫자 overflow·문자 인코딩·타겟별 저장 형식은 이 표만으로 확정하지 않습니다. 타입은 변수 선언뿐 아니라 필드, 매개변수, 반환값에도 사용합니다.

int count = 3;
var inferredCount = 3;

둘 다 초기값으로부터 정수를 보관합니다. var는 타입을 없애는 동적 변수 선언이 아니라 초기값에서 타입을 정하는 선언입니다. 메서드의 입력·출력과 공개 필드는 읽는 사람이 계약을 알아야 하므로 타입을 명시하는 것이 도움이 됩니다.

값이 없는 var value = null;은 어떤 타입인지 추론할 수 없어 현재 컴파일러가 거절합니다. var를 쓴 뒤 임의의 다른 타입을 대입할 수 있다고 생각하지 마세요.

현재 의미 검사에서 int 값을 long 변수에 전달하는 확대는 확인했습니다. 반대로 long 값을 int에 그대로 넣는 축소는 거절됩니다.

long wider = 42;
int invalid = 42L; // long을 int에 대입하므로 검사 오류

숫자 값이 작아 보이더라도 표현식의 타입은 별개입니다. 명시적 변환 문법을 확인하지 않고 다른 언어의 cast 구문을 가져오지 마세요.

타입 뒤의 ?는 null을 허용하는 형태입니다.

string? optionalName = null;
string invalidName = null; // null을 허용하지 않아 검사 오류

string?와 string은 같은 대입 계약이 아닙니다. 현재 검사기는 null 허용 문자열과 일반 문자열을 바로 연결하거나, null 허용 정수와 일반 정수를 바로 더하는 연산도 거절합니다. null 확인 이후의 자동 타입 좁히기와 관련 연산자 지원은 별도 확인 없이 가정하지 않습니다.

지역 변수는 선언한 블록의 범위에서 사용합니다. 반복문의 i는 현재 반복을, 밖에 선언한 total은 반복 사이의 누적 값을 표현합니다. 다른 메서드에서 지역 변수 이름을 그대로 사용할 수는 없습니다.

필드는 객체 또는 클래스에 속합니다. 매개변수와 필드의 이름이 같을 때는 this.value = value처럼 왼쪽의 현재 객체 필드를 명시합니다. 현재 의미 검사는 한 메서드 안에서 같은 지역 변수 이름을 다시 선언하면 중첩 블록 안에서도 거절합니다. 다른 블록에서는 nestedValue처럼 목적에 맞는 별도 이름을 사용하세요. 선언하지 않은 이름·중복 지역 선언·범위 밖 이름은 오류의 원인이 됩니다.

위치 또는 수식어 접근 의미
최상위 클래스·인터페이스에 수식어 없음 같은 패키지 안에서 사용하는 타입
클래스 멤버에 수식어 없음 기본 private 멤버
public 외부에 공개하는 타입·멤버
private 클래스 내부에서 사용하는 멤버
protected 상속 관계에서 사용하는 멤버

클래스가 공개되어도 생성자·필드·메서드가 모두 공개되는 것은 아닙니다. 다른 클래스에서 Counter(0)을 호출하려면 해당 생성자의 접근도 확인해야 합니다. 생성자 접근 오류와 객체 생성의 타입 오류를 구분하세요.

final 필드는 초기화 뒤 다시 대입하지 않는 계약을 표현합니다. 생성자에서 초기화하는 final 필드와 선언 시 초기화하는 final 필드를 구분하며, static final 필드는 선언 시 초기값이 필요하다는 검사 규칙을 확인했습니다. final이 객체 내부의 모든 값을 자동으로 불변으로 만드는 것은 아닙니다.

구조를 익혔다면 변수와 제어 흐름에서 실제 전체 소스의 계산 순서를 읽고 빌드·실행을 요청하세요. 현재 실행 확인 범위에서 실행 지원 한계를 함께 확인할 수 있습니다.