다트는 JIT(Just-In-Time)컴파일과 AOT(Ahead-Of-Time)컴파일을 모두 지원한다.
AOT 컴파일러는 다트 코드를 효율적인 네이티브 코드로 변환시킨다.
다트의 선택형 JIT 컴파일러는 핫 리로드를 지원한다. 따라서 빠른 개발 속도와 반복이 가능하다.
void main(){
var name = "Socrates";
var group = "Human";
print("$name is $group");
print("${name + " = " + group}")
}
var 타입은 변수 초기화(Initalize) 때 지정된 타입이 var 변수의 타입이 된다.
dynamic 타입은 변수 초기화와 무관하게 대입되는 모든 변수의 타입으로 변환이 가능하다.
void main() {
var varVariable = 'test';
varVariable = 1; // error
dynamic dynamicVariable = 'test';
dynamicVariable = 1;
}
다음과 같이 var 타입 변수에 아무런 값을 넣지 않으면 dynamic 타입으로 자동으로 변환된다.
void main() {
var name; // dynamic;
}
c#과 동일하게 <>로 타입을 지정하여 리스트를 만들 수 있다. 타입이 없으면 dynamic으로 지정된다. 특이한 점은 중괄호가 아닌 대괄호로 묶는다.
void main() {
List dynamicList = [ // dynamic list
1,
2,
3
];
List<int> intList =[
1,
2,
3
];
}
리스트와 마찬가지로 타입을 지정하지 않으면 dynamic 타입으로 지정된다.
void main() {
Map dictionary = {
'One' : 1,
'Two' : 2
};
Map<String,int> dictionary2={
'One' : 1,
'Two' : 2
};
dictionary2.addAll({'Three' : 4});
print(dictionary2);
dictionary2['Three']=3;
print(dictionary2);
dictionary2.remove('Three');
print(dictionary2);
}
{One: 1, Two: 2, Three: 4}
{One: 1, Two: 2, Three: 3}
{One: 1, Two: 2}
final, const 모두 값을 한 번 지정하면 변경할 수 없다는 건 동일하나,
const는 빌드 타임에 값이 지정되어 있어야하지만, final은 런타임에 지정해도 상관없다.
void main() {
final var1;
final int var2 = 2;
const var3;
const int var4 =4;
var1=1;
var3=3; // error
}
optional parameter들은 값을 전달하지 않았을 경우 사용할 수 있는 디폴트 값을 지정할 수 있다. 만약 지정하지 않았다면 null이 된다.
Optional position parameter
위치를 기반으로 값을 전달한다. []로 표시한다.
Optional named parameter
이름을 기반으로 값을 전달한다. 따라서 파라미터의 위치는 중요하지 않다. {}로 표시한다. 위치 기반으로 값을 전달할 수 없다.
void optPosFunc(int num1, [int num2=0]){
print('$num1, $num2');
}
void optNamedFunc(int num1, {int? num2}){
print('$num1, $num2');
}
void main() {
optPosFunc(1);
optNamedFunc(num2:0, 1);
}
1, 0
1, 0
delegate와 비슷하게 함수를 변수처럼 사용할 수 있게 해준다.
typedef Operation(int x, int y);
void add(int x, int y){
print(x+y);
}
void subtract(int x, int y){
print(x-y);
}
void calculate(int x, int y, Operation oper){
oper(x,y);
}
void main() {
calculate(1,2,add);
calculate(1,2,subtract);
}
3
-1
Named Constructor라고 클래스명.생성자명으로 생성자 오버로딩을 할 수 있다.
class Person{
String _name; //private
int age; // public
Person(name,age):this._name = name, this.age=age;
Person.fromMap(Map values) // Named Constructor
:this._name=values['name'],
this.age=values['age'];
void PrtInfo(){
print('$_name, $age');
}
}
void main() {
Person p1 = new Person('p1',11);
Person p2 = new Person.fromMap({
'name' : 'p2',
'age' : 11
});
p1.PrtInfo();
p2.PrtInfo();
}
p1, 11
p2, 11
다트에서는 인터페이스 키워드를 사용하지 않고 클래스를 인터페이스로 사용한다.
class PersonInterface{
void PrtInfo(){}
}
class Person implements PersonInterface{
String _name; //private
int age; // public
Person(name,age):this._name = name, this.age=age;
void PrtInfo(){
print('$_name, $age');
}
}
이전에 사용한 객체에 대해 일련의 작업을 수행할 수 있어 임시변수의 사용을 줄일 수 있다. 메서드 외 필드에도 적용이 가능하다. 중첩도 가능하다. 리턴 값이 없는 void 메서드에는 사용이 불가능하다.
new Person('p2',11)
..PrtInfo();
non-nullable한 변수의 초기화를 나중에 할 수 있다.
int func(){
return 1;
}
void main() {
late int a = func();
}
리스트에 대해 loop를 도는 메서드다. 인자로 시작할 인덱스, 이전 루프에서 리턴한 값, 현재 인덱스 값을 가진다.
void main() {
List<int> numbers=[
1,2,3,4,5
];
int total = numbers.fold(0,(total,element){
print('total: $total, element: $element');
return total+element;
});
print(total);
}
total: 0, element: 1
total: 1, element: 2
total: 3, element: 3
total: 6, element: 4
total: 10, element: 5
15
시작 인덱스를 받지 않고 무조건 인덱스 0번부터 시작하는 fold이다.
void main() {
List<int> numbers=[
1,2,3,4,5
];
int total = numbers.reduce((total,element){
print('total: $total, element: $element');
return total+element;
});
print(total);
}
total: 1, element: 2
total: 3, element: 3
total: 6, element: 4
total: 10, element: 5
15
reduce는 시작할 때 total 값이 첫 번째 인덱스 값이 들어간다. 따라서 해당 리스트의 타입만 이용할 수 있다.
예를 들어 String 리스트의 단어들의 길이를 다 더하는 루프를 만든다고 할 때, fold는 가능하지만 reduce는 첫 번째 값인 'test1'이 total에 들어가므로 에러가 난다. (string -> int 불가능)
void main() {
List<String> numbers=[
'test1',
'test2',
];
int total = numbers.fold(0,(total,element){
return total+element.length; // string -> int
});
print(total);
int total = numbers.reduce((total,element){ //이미 total에 string인 'test1'이 들어가 있음.
int로 변환 불가능.
return total+element.length; // string -> int
});
}
람다식과 똑같은 역할을 한다.
void main() {
List<String> numbers=[
'test1',
'test2',
];
int total = numbers.fold(0,(total,element) =>total+element.length);
print(total);
}
10
슈퍼 클래스의 메서드를 구현 없이 사용할 때 사용됨. 다중 상속이 불가능함.
슈퍼 클래스의 메서드를 필수적으로 구현해서 사용하도록 할 때 사용됨. 다중 상속이 가능함.
여러 클래스들의 메서드를 재사용하기 위해 사용됨.
상속처럼 보이지만 실제로는 하나의 슈퍼 클래스만 존재하기 때문에 상속이 아니다. 따라서 여러개의 클래스를 with로 받을 수 있다.
추상 클래스와 비슷한 개념으로 유사한 기능, 속성을 가진 다양한 클래스들의 메서드를 재사용할 수 있게 한다. mixin 클래스는 생성자가 없어야 한다.
mixin First {
void firstFunc(){
print('hello');
}
}
mixin temp {
void number(){
print(10);
}
}
mixin Third{
void thirdFunc(){
print('not override');
}
}
// mixin type used with keyword
class Second with First,Third, temp{
@override
void firstFunc(){
print('can override if needed');
}
}
void main(){
var second = Second();
second.firstFunc();
second.thirdFunc();
second.number();
}
can override if needed
not override
10