티스토리 뷰

반응형

변수 선언

다음은 변수를 생성하고 초기화하는 예제이다. 다트는 타입 추론을 지원하므로 name의 타입은 String이 된다.

var name = "Bob";

 

타입을 명시적으로 지정할 수도 있다.

String name = "Bob";

 

객체가 단일 타입에 제한되지 않으면 최상위 클래스인 Object 타입 또는 dynamic을 지정할 수 있다.

Object name = "Bob";

 

 

 

널 안전성

다트 언어는 널 안전성(null safety)을 적용한다. 널 안전성을 사용하므로 다음 3가지를 지켜야 한다.

  • null을 허용하려면 타입 선언 시?를 추가해서 널-가능 타입으로 만들어야 한다.
  • 널-불가능 타입은 사용하기 전에 변수를 초기화해야 한다. 널-가능 타입은 초기화를 하지 않으면 자동으로 null로 초기화된다.
  • 널-가능 타입은 일반적인 점(dot) 접근으로 속성에 접근하거나 메서드를 호출할 수 없다.

 

 

 

기본 값

초기화되지 않은 널-가능 변수의 기본값은 null이다.

int? lineCount;  // lineCount == null

 

널-불가능 변수는 초기화를 해야 한다.

int lineCount = 0;

 

단, 로컬 변수는 선언 시 변수를 초기화할 필요는 없지만, 꼭 변수가 사용되기 전에 값을 할당해야 한다.

다음 코드는 lineCount가 print()에 사용되는 시점에 null이 아니므로 유효한 코드가 된다.

void initialTest() {
	int lineCount;
	
	if (weLikeToCount) {
	  lineCount = countLines();
	} else {
	  lineCount = 0;
	}
	
	print(lineCount);
}

 

하지만 최상위 변수와 클래스 변수는 무조건 초기화를 해야 한다.

 

 

 

Late 변수

late 수식어는 다음 세 가지 용도로 사용된다.

  • 선언 후에도 초기화가 가능한 널-불가능 변수 만들기
  • 변수를 지연 초기화하기
  • 인스턴스 변수의 초기값이 this에 접근해야 하는 경우

 

선언 후에도 초기화가 가능한 널-불가능 변수 만들기

다음 코드에서 description은 최상위 변수이므로 무조건 초기화를 해야 한다. 하지만 late를 사용하여 초기화를 하지 않아도 된다. 단, late 변수를 초기화하지 않고 변수가 사용되면 런타임 에러가 발생한다. 그러니 사용되기 전에 꼭 초기화를 하자.

late String description;

void main() {
  description = 'Feijoada!';
  print(description);
}

 

 

 

변수를 지연 초기화하기

변수를 late로 만들었지만 선언과 동시에 초기화를 하면, 초기화 코드는 변수가 처음 사용될 때 실행된다. 이를 지연 초기화라고 한다.

 

다음 코드에서 temperature 변수가 사용될 때, readThermometer()가 호출된다.

late String temperature = readThermometer(); // 지연 초기화됨.

 

 

인스턴스 변수의 초기값이 this에 접근해야 하는 경우

인스턴스 변수의 초기값은 this에 접근할 수 없다. this는 해당 인스턴스의 인스턴스 속성과 인스턴스 메서드에 해당한다. 이런 경우 late를 사용해서 해결할 수 있다.

 

다음 코드에서 square는 this인 num을 접근할 수 없지만, late를 사용하므로 가능해진다.

class LateTest {
  int num;
  late int square = num * num;  // 성공
  LateTest(this.num);
}

 

 

 

 

상수 (Final과 Const)

변경할 계획이 없는 변수는 final 또는 const를 사용할 수 있다.

 

 

final 상수

final 상수는 한 번만 설정할 수 있으며, 런타임 상수이다. 런타임 상수는 실행 중에 상수로 만들어 사용한다. 이는 런타임(실행) 시 값을 결정하여 상수로 만들 수 있다는 장점이 있다.

final name = 'Bob'; // 타입 주석 없음
final String nickname = 'Bobby';

 

final 상수는 선언 시 초기화를 하지 않아도 된다. 하지만 한 번만 설정이 가능하고, 사용 전에 꼭 초기화를 해야 한다.

final String name;
name = 'Bob';

 

 

인스턴스 상수는 final를 사용할 수 있지만, const는 사용할 수 없다. 반면, 클래스 상수는 const를 사용할 수 있다.

class TestClass {
  final num1 = 10;  // 인스턴스 변수는 final 가능
  // const num2 = 10;  // 인스턴스 변수는 const 불가능
  static const num3 = 10; // 클래스 변수는 const 가능
}

 

 

 

 

const 상수

const 상수는 컴파일 시점에 상수가 된다. 상수 숫자나 문자열 리터널, const 상수 또는 상수 숫자의 산술 연산의 결과와 같은 컴파일 타임 상수로 값을 설정해야 한다.

const name = 'Bob';  // 문자열 리터널
const bar = 1000000; // 상수 숫자
const double atm = 1.01325 * bar; // 상수 숫자의 산술 연산의 결과

 

const 키워드는 상수 값을 만드는 데에도 사용할 수 있다.

 

다음은 빈 상수 리스트(const [])를 만들어 사용한 예제이다.

var foo = const [];
final bar = const [];
const baz = const [];


상수 선언의 초기화 식에서 const를 생략할 수 있으니, const를 중복으로 사용할 필요가 없다.

const baz = [];  // const baz = const []; 과 동일함

 

const 값을 가지고 있더라도 변수라면 값을 변경할 수 있다.

var foo = const [];
foo = [1, 2, 3];

타입 체크(is) 및 타입 변환(as), 컬렉션 if, 스프레드 연산자(... 와 ...?)를 사용하는 상수를 정의할 수 있다.

const Object i = 3;
const list = [i as int];
const map = {if (i is int) i: 'int'};
const set = {if (list is List<int>) ...list};

 

final 객체는 수정할 수 없지만, 속성은 변경할 수 있다. 반면, const 객체는 속성도 변경할 수 없다.

class Person {
  String name;
  Person(this.name);
}

void main() {
  final user1 = Person("User1");
  user1.name = "None";  // 성공
}

 

 

 

final 상수와 const 상수의 차이점

컴파일 시점에서 상수 값이 결정된다면 const 상수를 사용하는 것이 좋다.

 

값 결정 시점

final : 런타임에 한 번만 값을 할당할 수 있다.

const : 컴파일 타임에 값을 할당하며, 상수로 초기화되어야 한다.

 

사용 가능성

final : 복잡한 초기화가 필요하거나, 런타임에 값이 결정되는 경우 유용하다.

const : 컴파일 타임에 확정된 불변 값을 정의할 때 유용하다.

 

메모리 사용:

final : 런타임에 결정되므로, 각 인스턴스마다 메모리를 차지한다.

const :  컴파일 타임에 결정되므로, 동일한 메모리 위치를 사용한다.

반응형
댓글
공지사항