var name = '변수 값'; // 1
String name = '변수 값'; // 2
final name = '변수 값' // 3
late final String name; // 4
const a = 6; // 5
1) 변수의 타입을 구체화 할 필요 없다. Dart 컴파일러가 변수의 타입을 추측해서 알아낸다.
2) 변수 타입을 명시적으로 정해줄 수도 있다.
3) 이후에 수정이 될 수 없다. const와 유사한 역할.
4) 초기 데이터 없이 변수를 선언 할 수 있게 해준다.
5) js의 const와 다르다! dart의 const는 compile-time constant를 만든다. 컴파일 전에 값을 무조건 알면 사용하는 타입.
관습적으로 함수나 메소드 내부에 지역 변수를 선언할 때에는 var를 사용한다. Class에서 변수나 Property를 선언할 때에는 타입을 지정한다.
var name; 또는 dynamic name;
name = 'String';
name = 1;
name = true;
위와 같이 변수가 타입이 지정되지 않은 Dynamic한 경우에는 어떠한 값이라도 들어갈 수 있게 된다. 위 타입은 추천되는 방식은 아니지만 때때로 유용하게 사용된다.
void main() {
String? name = 'John'; // null이 들어 갈 수 있는 변수 name
name?.isNotEmpty; //name이 null이 아니라면 not Empty
}
위처럼 변수에 ?를 추가 하여 해당 변수에 null이 들어갈 수 있음을 명시 해준다. 만약 ?가 없는 경우에는 해당 변수에 null 값이 들어갈 수 없다. 이를 통해 null 값 참조를 통한 런타임 에러가 발생하는 것을 방지한다.
Dart에서는 모든 자료형, 함수들은 object로 이루어져있다.
Class로 이루어져있기 때문에 import 없이 다양한 메서드를 사용할 수 있다.
기본 자료형 : String, boolean, int, double, num (int or double)
복잡한 자료형 : Lists, String Interpolation, Collection, Maps, Sets
var numbers = [1, 2, 3, 4];
List<int> numbers = [1, 2, 3, 4]; // 1,2 둘 다 동일하다
void main() {
var giveMeFive = true;
var numbers = [1, 2, 3, 4, if(giveMeFive) 5];
print(numbers);
}
>> [1,2,3,4,5]
만약 collection if에 해당하는게 true라면 해당 값을 추가한다.
var name = 'John';
var age = 20;
var greeting = 'Hi, My name is $name and I\'m ${age + 1}';
print(greeting);
>> Hi, My name is John and I'm 21
void main() {
var oldFriends = ['nic0', 'lynn'];
var newFriends = [
'lewis',
'ralph',
'darren',
for (var friend in oldFriends) "OG $friend" ];
print(newFriends);
}
>> [lewis, ralph, darren, OG nic0, OG lynn]
다른 배열에 있는 값을 배열 안에 넣어준다.
var players1 = {
'name' : 'John',
'xp' : 20.99,
'superPower' : false,
};
Map<int, bool> players2 = {
1: true,
2: false,
3: true
};
print(players2);
다른 언어에서의 Map과 동일한 역할, 키/값으로 값을 저장
var players = {1,2,3,4,5};
Set<int> numbers = {1,2,3,4,5};
numbers.add(1);
print(numbers);
>> {1, 2, 3, 4, 5}
list와는 다르게 sets는 순서가 있으며, 모든 요소는 유니크하다. (Python의 tuple)
1. 선언
String sayHello(String name){
return "Hello $name nice to meet you !";
}
void main() {
print(sayHello('Kim'));
}
>> Hello Kim nice to meet you !
num plus(num a, num b) => a + b; // A
void main() {
print(plus(4,5));
}
>> 9
A줄의 코드처럼 코드가 1줄인 경우 fat Arrow Syntax를 통해 간결하게 작성할 수 있다.
2. Named Parameter
여러 매개변수를 넘겨줄 때, 순서에 관계 없이 argument의 이름을 입력하여 전달. 기존 position parameter 처럼 순서를 외울 필요가 없다.
String hello({String name = 'anon', int age = 20, String country = 'Korea'}){
return "Hi, I'm $name, and I am $age years old. I'm from $country.";
}
void main() {
print(hello(
name: "John",
age: 8,
country : "korea",
));
}
>> Hi, I'm John, and I am 8 years old. I'm from korea.
위 방법은 사용자가 값을 입력하지 않는 상황을 대비하여 named parameter에 defalut 값을 지정해주거나, required Modifier를 사용하여야한다.
String hello({required String name, required int age, required String country}){
return "Hi, I'm $name, and I am $age years old. I'm from $country.";
}
void main() {
print(hello(
name: "John",
age: 8,
country : "korea",
));
}
required 라는 수식어를 매개변수 앞에 추가하여 해당 매개 변수가 필수임을 알리고, 이후에 만약 해당 함수를 사용할 때 required 매개변수가 입력되지 않으면 해당 코드는 Dart가 컴파일 하지 않는다.
3. Optional Positional Parameter
String hello(
String name, int age, [String? country = 'Korea']) {
return "Hi, I'm $name, and I am $age years old. I'm from $country.";
}
void main() {
print(hello("John",8));
}
>> Hi, I'm John, and I am 8 years old. I'm from Korea.
위처럼 만약 Country라는 값만 기본 값을 설정해두고 nullable 하게 정하고 싶으면 대괄호를 사용하여 지정할 수 있다.
4. QQ Operator
String capitalizeName(String? name) => name != null ? name.toUpperCase() : 'ANON';
String capitalizeName(String? name) => name.toUpperCase() ?? 'ANON';
void main() {
print(capitalizeName('John'));
print(capitalizeName(null));
}
>> JOHN
ANON
첫 번째 삼항 연산자를 사용한 코드와 두 번째 QQ 연산자를 사용한 코드는 동일하다.
만약 left ?? right 일때, left의 값이 null 이면 right를 return하고, 아니면 left를 return 한다.
void main() {
String? name;
name ??= 'John';
name ??= 'Ben';
print(name);
}
>> John
위 QQ equals는 만약 해당 변수의 값이 null인 경우에만 assign 하도록 한다.
5. typedef
typedef ListOfInt = List<int>;
ListOfInt reversedList(ListOfInt list) => (list.reversed).toList();
void main(){
print(reversedList([1,2,3,4,5]));
}
>> [5, 4, 3, 2, 1]
위처럼 자주 사용되는 자료형은 alias (별칭)을 지정하여 작성할 수 있다.
- class
class Player{
final String name;
int xp;
Player(this.name,this.xp);
void sayHello(){
print(name + " and " + xp.toString());
}
}
void main(){
var player = Player("Kim", 25);
player.sayHello();
}
클래스의 프로퍼티들은 명시적으로 type을 지정해주어야 한다.
위 코드 처럼 생성자를 간결하게 작성할 수 있다.
class Player{
final String name;
int xp;
String team;
int age;
Player({
required this.name,
required this.xp,
required this.team,
required this.age}
);
void sayHello(){
print(name + " and " + xp.toString() + " and " + team + " and " + age.toString());
}
}
void main(){
var player = Player(
name :"Kim",
xp : 25,
team: "team blue",
age: 20,
);
player.sayHello();
}
함수와 동일하게 named parameter를 적용할 수 있다.
Player({
required this.name,
required this.xp,
required this.team,
required this.age}
);
Player.createBluePlayer({required String name, required int age}) :
this.age = age,
this.name = name,
this.team = 'Blue',
this.xp = 0;
위처럼 특정 값만 입력하고 default값을 주고 싶으면 Name constructor를 사용하면 가능하다.
- Cascade Operator
class Player {
String name;
int xp;
String team;
Player(
{required this.name,
required this.xp,
required this.team,
});
void sayHello() {
print(name +
" and " +
xp.toString() +
" and " +
team);
}
}
void main() {
var John = Player(name : 'John', xp : 1200, team : 'Red')
..name = 'Ben'
..xp = 1200000
..team = 'blue'
..sayHello();
}
- Enum
enum Team {red, blue}
class Player {
String name;
int xp;
Team team;
Player(
{required this.name,
required this.xp,
required this.team,
});
}
실수 방지를 위해 Enum 타입으로 선언할 수 있다.
- 추상 클래스
다른 클래스들이 직접 구현해야하는 메소드들을 모아둔 일종의 BluePrint (청사진)이다.
enum Team {red, blue}
abstract class Human{
void walk(){
print('I walk slow');
}
}
class Runner extends Human{
String? name;
int? xp;
Team? team;
Runner(
{required this.name,
required this.xp,
required this.team,
});
void walk(){
print('I walk fast.');
}
}
void main(){
var man = Runner(name : 'Ben', xp : 120, team : Team.blue)
..name = 'John'
..xp = 1200
..team = Team.red
..walk();
}
- 상속
enum Team {red, blue}
abstract class Human{
final String name;
Human({required this.name});
void walk(){
print('I walk');
}
}
class Runner extends Human{
final Team team;
Runner({
required this.team,
required String name
}) : super(name: name);
@override
void walk(){
super.walk();
print('faster than others.');
}
}
void main(){
var man = Runner(team : Team.blue, name : 'John');
man.walk();
}
위처럼 추상클래스를 만들어 상속시킬 수 있다. 이때 부모 클래스의 변수나 메소드를 수정 및 상속 할 경우에는 super라는 키워드를 사용하여야한다.
부모 클래스의 생성자를 호출할 경우에는 super(name : name) 처럼 부모 클래스의 생성자에게 전달을 해주어야한다.
- Mixin -
class Strong{
final double strength = 5000.0;
}
class StrongMan with Strong{
}
위 처럼 생성자가 없는 클래스를 Mixin 클래스로 With 키워드를 통해 해당 클래스의 프로퍼티를 가져올 수 있다. -> 클래스의 재사용성이 증가.