콘텐츠로 이동

문장

메서드와 생성자의 본문은 문장의 나열입니다. 이 장의 문장 외의 형태는 Unsupported method statement: … 오류입니다.

  • 단순 문장(선언·대입·호출·return·throw·break·continue·print)은 세미콜론으로 끝냅니다. 빠뜨리면 Method statement must end with semicolon: print("x") 오류입니다.
  • if·while·for·try처럼 블록으로 끝나는 문장 뒤에는 세미콜론을 쓰지 않습니다.
  • do { … } while (조건);은 세미콜론으로 끝냅니다.
  • 독립된 블록 { … }만 따로 쓸 수는 없습니다. 범위를 나누려면 메서드로 분리합니다.
  • 식만 있는 문장은 호출·생성자 호출·await만 허용합니다. a + b;, x;는 거절됩니다.
지역 변수 형태
[final] 타입 이름 = 초기값;
[final] var 이름 = 초기값;
int count = 0;
var name = "Kim";
final double rate = 0.5;
List<string> names = [];
string? note = null;
  • 초기값이 필요합니다. int x;는 Unsupported method statement: int x 오류입니다. 나중에 값을 정한다면 int x = 0;처럼 기본값으로 선언합니다.
  • 한 문장에 변수 하나만 선언합니다(int a = 1, b = 2;는 거절).
  • final 지역 변수는 다시 대입하면 Cannot assign to final variable 'rate'. 오류입니다. 지역 변수에 const는 쓸 수 없습니다.
  • void 지역 변수는 Local variable must not use void type. 오류입니다.
  • 초기값에서 자기 자신을 참조할 수 없습니다 (Local variable 'x' cannot reference itself in its initializer.). 문자 하나짜리 변수 이름과 숫자 접미사가 겹치는 float f = 1.5f; 같은 경우도 이 오류가 날 수 있으므로 float ratio = 1.5f;처럼 의미 있는 이름을 사용하세요.
  • 지역 변수는 선언한 뒤부터, 선언한 블록 안에서만 사용합니다 (Undefined variable 'x'.).
  • 한 메서드 안에서는 중첩 블록이어도 같은 이름을 다시 선언할 수 없습니다 (Duplicate local variable 'i' in method 'main'.). 서로 독립된 블록(예: 두 개의 연속된 for)에서는 같은 이름을 다시 쓸 수 있습니다.
  • 매개변수와 같은 이름의 지역 변수는 선언할 수 없습니다. 필드와 같은 이름은 가능하며 이때 필드는 this.이름으로 구분합니다.
count = count + 1;
this.name = name;
order.total = 0;
items[0] = "first";
ages["Kim"] = 31;
  • 왼쪽에는 변수, 객체.필드, 리스트[인덱스], 맵[키]를 씁니다. this, super에는 대입할 수 없습니다.
  • 오른쪽 값의 타입은 왼쪽에 대입 가능해야 합니다 (Cannot assign int to string target 'name'.).
  • 복합 대입·증감 연산자는 미지원입니다.
쓰지 않는 형태 대신 쓰는 형태
x += 2; x -= 2; x *= 2; x = x + 2; x = x - 2; x = x * 2;
i++; i--; ++i; i = i + 1; i = i - 1;
a = b = 0; b = 0; a = 0;
if 형태
if (조건) {
…
} else if (조건) {
…
} else {
…
}
  • 조건은 괄호로 감싸고 본문은 반드시 중괄호로 감쌉니다. if ready { … }는 Invalid if statement syntax., if (ready) run();은 If statement must use a block body. 오류입니다.
  • 조건은 bool 식이어야 합니다 (If condition must be boolean but got int.). bool?도 허용하지 않습니다.
  • else if는 개수 제한 없이 이어 쓸 수 있습니다.
while (remaining > 0) {
remaining = remaining - 1;
}

본문을 실행하기 전에 조건을 검사합니다. 괄호·중괄호·bool 조건 규칙은 if와 같습니다.

do {
attempts = attempts + 1;
} while (attempts < 3);

본문을 먼저 한 번 실행한 뒤 조건을 검사합니다. 마지막의 while (조건);과 세미콜론을 빠뜨리지 마세요.

for 형태
for (초기화; 조건; 갱신) {
…
}
for (int i = 0; i < names.size(); i = i + 1) {
print(names[i]);
}
  • 세 부분을 세미콜론으로 구분합니다. 각 부분은 비워 둘 수 있으며 조건을 비우면 항상 참입니다(for (;;) { … }).
  • 초기화는 지역 변수 선언 또는 대입, 갱신은 대입문만 사용합니다. i++는 Unsupported for action syntax, 다른 형태는 For update must be an assignment statement. 오류입니다.
  • 초기화에서 선언한 변수는 해당 for 안에서만 사용합니다.
  • 컬렉션을 순회할 때는 아래의 for (var item in items)를 사용할 수 있습니다. Java의 콜론 형식 for (Item item : items)는 사용하지 않습니다.
전체 소스: Loops.uni
class Loops {
static void main() {
List<string> names = ["Kim", "Lee", "Park"];
for (int i = 0; i < names.size(); i = i + 1) {
print(names[i]);
}
Set<string> tags = {"a", "b"};
List<string> tagList = tags.toList();
for (int j = 0; j < tagList.size(); j = j + 1) {
print(tagList[j]);
}
Map<string, int> ages = {"Kim": 30, "Lee": 25};
List<MapEntry<string, int>> entries = ages.entries();
for (int k = 0; k < entries.size(); k = k + 1) {
print(entries[k].key + "=" + entries[k].value);
}
}
}
List<string> names = ["Kim", "Lee"];
for (var name in names) {
print(name);
}
  • 대상은 List<T>, Set<T> 또는 Deque<T>입니다. 그 밖의 값은 for-in iterates a List, Set or Deque, not <type>. 오류입니다. Map.keys(), values(), entries()가 반환한 목록도 순회할 수 있습니다.
  • 요소 변수는 var 또는 요소를 받을 수 있는 명시적 타입으로 선언합니다. 대상 식은 한 번 평가하고 컬렉션 순서로 방문합니다.
  • 본문에서 순회 중인 컬렉션의 항목을 추가·삭제하지 마세요. break와 continue는 다른 반복문과 같습니다.
  • 현행 콘솔·서버 생성기는 각 대상의 순회 문법으로 변환합니다. 등록 원본을 처리하는 서버 compiler가 다르면 거절될 수 있으므로 해당 응답의 진단을 확인합니다.
for (int i = 0; i < 10; i = i + 1) {
if (i == 2) {
continue;
}
if (i == 5) {
break;
}
}
  • break;는 가장 안쪽 반복을 끝내고, continue;는 다음 반복으로 넘어갑니다. 반복 밖에서는 Break statement is only valid inside a loop. 오류입니다.
  • 레이블(break outer;)은 미지원입니다. 중첩 반복을 한 번에 끝내려면 bool 변수와 조건을 사용하거나 메서드로 분리해 return합니다.
return; // void 메서드
return value; // 값을 반환하는 메서드
상황 진단
void 메서드에서 값 반환 Void method 'm' must not return a value.
값 반환 메서드에서 return; Method 'm' returning int must return a value.
일부 경로에 반환 없음 Method 'f' must return int.
반환 타입 불일치 Cannot return long from method 'f' returning int.

return, throw, break, continue 뒤나 끝나지 않는 반복 뒤에 같은 블록의 문장을 두면 거절됩니다.

오류 예
static int f() {
return 1;
print("x"); // Unreachable statement after return: print("x")
}
throw Exception("invalid amount");
  • 던지는 값은 Exception 또는 그 자식 클래스의 객체여야 합니다 (Throw expression must be Exception but got string.).
  • 기본 타입 Exception과 하위 타입 ArgumentException, StateException, IndexException, IOException을 사용합니다. message로 메시지를 읽습니다. 표준 API 오류의 구체적인 하위 타입과 문구는 대상별로 다를 수 있습니다.
  • 사용자 예외는 Exception을 상속한 클래스로 만듭니다. 메서드에 throws 절은 쓰지 않습니다.
try 형태
try {
…
} catch (예외타입 이름) {
…
} catch (다른예외타입 이름) {
…
} finally {
…
}
전체 소스: Payments.uni
class PaymentException extends Exception {
public PaymentException(string message) {
this.message = message;
}
}
class Payments {
static int charge(int amount) {
if (amount < 0) {
throw PaymentException("negative amount");
}
return amount;
}
static void main() {
try {
int charged = charge(5);
print("charged " + charged);
} catch (PaymentException error) {
Console.error(error.message);
} catch (Exception error) {
Console.error("runtime");
}
}
}
  • try에는 하나 이상의 catch 또는 finally가 필요합니다. catch의 형태는 catch (타입 이름)이며 catch (e)처럼 타입을 생략할 수 없습니다.
  • catch 타입은 Exception을 상속해야 합니다 (Catch type 'Socket' must extend Exception.).
  • catch 변수는 해당 catch 블록 안에서만 사용합니다. catch 블록마다 다른 변수 이름을 쓸 필요는 없습니다.
  • catch는 선언 순서대로 검사하고 해당 타입과 하위 타입을 처리합니다. 어느 catch에도 맞지 않는 예외는 호출자에게 전달됩니다.
  • finally는 마지막 절에 쓰며 정상 완료·예외·return·break·continue로 빠져나갈 때 실행합니다. try { … } finally { … } 형태도 가능합니다.
  • 네이티브 실행은 throw·try/catch/finally를 지원하며 범위 밖 인덱스·0 나눗셈 등 런타임 오류도 Exception으로 처리할 수 있습니다. 처리하지 않은 예외는 Unibase runtime error: <메시지>와 안쪽 메서드부터 최대 64줄의 at <메서드> (<경로>:<줄>) 위치를 출력하고 종료 코드 1로 끝납니다.
  • 현행 8개 콘솔·서버 생성 대상은 예외 문장을 각 언어의 예외 처리로 변환합니다. 일반 catch는 Exception으로 작성하며 경계값이 오류가 되는지와 오류 문구는 대상의 동작을 따릅니다. 실제 배포 서버의 결과는 해당 변환 진단에서 확인합니다.
print(42);
print("total=" + total);
  • print(식);은 값 하나를 한 줄로 출력하는 내장 문장입니다. 문자열이 아닌 값도 그대로 전달할 수 있습니다.
  • print는 식이 아니라 문장입니다. 값을 반환하지 않으며 인수는 하나만 받습니다.
  • print의 인수는 일부 의미 검사를 건너뛰므로, 타입·접근·오타를 확인하려면 값을 먼저 타입이 있는 변수에 담아 검사합니다.
  • Console.write와 Console.error는 string을 받습니다. 숫자는 Console.write("" + count)처럼 변환하며 한 줄 출력에는 print(count)를 사용합니다.
string value = await future; // Future<string>의 결과
await completed; // Future<void>의 완료 대기
  • await의 대상은 Future<T>입니다. Future<void>의 await는 값을 만들지 않으므로 독립 문장으로 사용합니다. 실패로 완료된 Future는 기다리는 지점에서 예외를 전달합니다.
  • async 메서드 수식어는 없습니다. 작업은 Async와 비동기 작업의 Async.run으로 시작하거나 Completer로 완료를 전달합니다.

대문자 SELECT, INSERT, UPDATE, DELETE, CREATE로 시작하는 문장은 SQL 문장입니다. 자세한 형태는 내장 언어와 schema에 있습니다.

string _name = "Kim";
INSERT INTO audit (name) VALUES(_name);
문장 대체 방법
switch, match, when if / else if / else
for (T x : xs), foreach for (T x in xs) 또는 인덱스 for; 대상별 생성 제한 확인
using, try-with-resources 지원 대상에서 try/finally로 정리
레이블 break·continue bool 변수 또는 메서드 분리
지역 함수 선언 클래스의 메서드. 함수 값을 전달할 때는 람다 사용
goto, yield, defer 없음