상세 컨텐츠

본문 제목

Dart - Class #3

Dart

by techbard 2024. 1. 10. 23:19

본문

반응형
void main(List<String> args) {
  var p1 = new Point(x: 10, y: 20);
  var p2 = new Point(x: 100, y: 200);

  print(Point.counter); // 2
}

/*
A field is bound to a specific instance of a class. 
It means that each instance of the class has separate field values. 
So when you change the field value of one instance, 
it doesn’t affect other instances. 

Because of this, a field is often 
called an instance variable or instance field.

Unlike an instance field, a static field is bound to a class, 
not a specific instance of the class. 
It means that a static field is shared by all instances of the class.

note: If the _variable is a static field, you can mark the getter as static.
*/

class Point {
  int x;
  int y;
  Point({this.x = 0, this.y = 0}) {
    _counter++;
  }
  static int _counter = 0;
  static int get counter => _counter;
}

// 결과
2​
void main(List<String> args) {
  final x1 = X(x: 1, y: 1);
  final x2 = X(x: 2, y: 2);

  var isIdentical = identical(x1, x2);
  print("Isidentical: $isIdentical");

  x1
    ..x = 10
    ..y = 11;

  x1.show();
}

class X {
  int x;
  int y;

  X({
    required this.x,
    required this.y,
  });

  void show() {
    print("x: $x y: $y");
  }
}

// 결과
Isidentical: false
x: 10 y: 11

 

void main(List<String> args) {
  final person1 = Person(name: 'John', age: 10);
  Person person2 = Person(name: 'John', age: 10);
  var person3 = Person(name: 'John', age: 10);
  person1;
  person2;
  person3;

  const cPerson1 = ConstPerson(name: 'John', age: 100);
  const cPerson2 = ConstPerson(name: 'John', age: 100);

  const cPerson3 = ConstPerson(name: 'John', age: 10);
  const cPerson4 = ConstPerson(name: 'John', age: 11);
  print("Is identical: ${cPerson1 == cPerson2}");
  // 상수 생성자를 써도 멤버가 다르면 다른 객체다.
  print("Is identical: ${identical(cPerson3, cPerson4)}");

  const noname1 = ConstPerson.noname1();
  print("noname1's name: ${noname1.name} age: ${noname1.age}");

  const noname2 = ConstPerson.noname2(20);
  print("noname2's name: ${noname2.name} age: ${noname2.age}");

  const noname3 = ConstPerson.noname3();
  print("noname3's name: ${noname3.name} age: ${noname3.age}");

  const nonameOther = ConstPerson.other();
  print("noname other's name: ${nonameOther.name} age: ${nonameOther.age}");
}

class Person {
  final String name;
  final int age;

  Person({required this.name, required this.age});
}

class ConstPerson {
  final String name;
  final int age;

  const ConstPerson({required this.name, required this.age});
  const ConstPerson.noname1({this.name = 'noname', this.age = 0});
  const ConstPerson.noname2(this.age) : name = "that name";
  const ConstPerson.noname3()
      : name = 'unknown name',
        age = 0;
  const ConstPerson.other({
    String? name,
    int? age,
  })  : name = name ?? 'other name',
        age = age ?? 30;
}
 
// 결과
Is identical: true
Is identical: false
noname1's name: noname age: 0
noname2's name: that name age: 20
noname3's name: unknown name age: 0
noname other's name: other name age: 30

 

void main(List<String> args) {
  final v = Vehicle(4);
  print(Car());
  print(Bicycle());
}

class Vehicle {
  final int wheelCount;
  const Vehicle(this.wheelCount);

  @override
  String toString() {
    // if (runtimeType == Vehicle) {
    //   return 'Vehicle with $wheelCount wheels';
    // } else {
    //   return super.toString();
    // }
    return '$runtimeType with $wheelCount wheels';
  }
}

class Car extends Vehicle {
  const Car() : super(4); 
  /* const Car(super.wheelCount); */
}

class Bicycle extends Vehicle {
  const Bicycle() : super(2);
}

// 결과
Car with 4 wheels
Bicycle with 2 wheels

 

void main(List<String> args) {
  final p = Person(firstName: 'John', lastName: 'Doe');
  print(p.fullName);
  print(p.fullNameGetter);
}

class Person {
  final String firstName;
  final String lastName;
  final String fullName;

  // 실행 될 때마다 호출
  String get fullNameGetter {
    print("getter is called.");
    return "$firstName $lastName";
  }

  const Person({
    required this.firstName,
    required this.lastName,
  }) : fullName = "$firstName $lastName"; // 객체 생성 시점에만 한 번 호출
}

// 결과
John Doe
getter is called.
John Doe

 

void main(List<String> args) {
  final car = Car();
  car.drive(speed: 20);
  print("Speed is ${car.speed}");
  car.drive(speed: 40);
  car.stop();
}

class Car {
  int _speed = 0;

  drive({required int speed}) {
    _speed = speed;
    print("Accelarating to $speed km/h");
  }

  int get speed => _speed;

  stop() {
    _speed = 0;
    print("Stopping...");
    print("Stopped");
  }
}

// 결과
Accelarating to 20 km/h
Speed is 20
Accelarating to 40 km/h
Stopping...
Stopped

 

void main(List<String> args) {
  final newCar = NewCar();
  try {
    newCar.drive(speed: 10);
    newCar.drive(speed: -1);
  } catch (e) {
    print(e);
  }
}

class NewCar {
  int _speed = 0;

  int get speed => _speed;
  set speed(int newSpeed) {
    if (newSpeed < 0) {
      throw Exception('Speed cannot be negative.');
    }
    _speed = newSpeed;
  }

  drive({required int speed}) {
    this.speed = speed;
    print("Accelarating to $speed km/h");
  }

  stop() {
    speed = 0;
    print("Stopping...");
    print("Stopped");
  }
}

// 결과
Accelarating to 10 km/h
Exception: Speed cannot be negative.

 

void main(List<String> args) {
  print("Before instantiated: ${ICar._icarInstantiated}");
  ICar(name: 'my car');
  print("After instantiated: ${ICar._icarInstantiated}");
  ICar(name: 'your car');
  print("Another instantiated: ${ICar._icarInstantiated}");
}

class ICar {
  // 아래와 같이 static으로 정의해서 쓸 일이 거의 없다.
  // 만일 이걸 쓰게 된다면 뭐가 잘못되고 있는 것이다.
  static int _icarInstantiated = 0;
  static int get icarInstantiated => _icarInstantiated;
  static void _incrementICarInstantiated() => _icarInstantiated++;
  final String name;

  ICar({required this.name}) {
    _incrementICarInstantiated();
  }
}

// 결과
Before instantiated: 0
After instantiated: 1
Another instantiated: 2

 

// Factory constructors

void main(List<String> args) {
  print(Vehicle.car());
  print(Vehicle.truck());
}

class Vehicle {
  const Vehicle();

  // sub 클래스의 객체 생성 가능
  factory Vehicle.car() => Car();
  factory Vehicle.truck() => Truck();

  @override
  String toString() => "Vehicle of type $runtimeType";
}

class Car extends Vehicle {
  const Car();
}

class Truck extends Vehicle {
  const Truck();
}

// 결과
Vehicle of type Car
Vehicle of type Truck

 

void main(List<String> args) {
  final mom1 = Person(role: Role.mom);
  print(mom1.role);

  final mom2 = Mom(role: Role.mom);
  print(mom2.role);

  final dad1 = Person(role: Role.dad);
  print(dad1.role);

  // 이렇게는 호출할 수 없음.
  // final dad2 = Dad(role: Role.dad);
  // print(dad1.role);

  final dad2 = Dad();
  print(dad2.role);
}

enum Role { mom, dad, son, daughter, grandpa, grandma }

class Person {
  final Role role;

  const Person({
    required this.role,
  });
}

class Mom extends Person {
  // 아래 Dad 생성자와 비교
  const Mom({required super.role});
}

class Dad extends Person {
  // 위 Mom 생성자와 비교
  const Dad() : super(role: Role.dad);
}

// 결과
Role.mom
Role.mom
Role.dad
Role.dad

 

void main(List<String> args) {
  const car = Car();
  car.accelerate(); // abstract를 상속 받아 그대로 사용 가능하다.

  const motoCycle = Motocycle(); // implements로 새로 구현도 가능하다.
  motoCycle.accelerate();
  print(motoCycle.kind);
}

enum VehicleKind { car, truck, motocycle, bycycle }

abstract class Vehicle {
  final VehicleKind kind;
  const Vehicle({required this.kind});

  accelerate() => print("$kind is accelerating.");
  decelerate() => print("$kind is decelerating.");
}

class Car extends Vehicle {
  const Car() : super(kind: VehicleKind.car);
}

class Motocycle implements Vehicle {
  const Motocycle();
  
  @override
  accelerate() => print("Motocycle is accelerating.");

  @override
  decelerate() => print("Motocycle is decelerating.");

  @override
  VehicleKind get kind => VehicleKind.motocycle;
}

// 결과
VehicleKind.car is accelerating.
Motocycle is accelerating.
VehicleKind.motocycle

 

// ignore_for_file: public_member_api_docs, sort_constructors_first, unnecessary_getters_setters

// use classes to define new types
// we are used to int, double, String, bool, List, Map, Set, Function
// classes - blueprint for objects
// object - container that holds some data (functionality to manipulate the data)

// Composition (has-a relationship)

void main(List<String> args) {
  var course = Course(
    name: "Dart Programming",
    book: Book(bookName: "Advanced Dart Programming", isbn: '123-456'),
    instructor: Instructor(name: 'John Doe'),
  );

  print(course);
}

class Instructor {
  final String _name;

  Instructor({required String name}) : _name = name;

  String get name => _name;

  @override
  String toString() => "Name of Instructor: $_name";
}

class Book {
  String _bookName;
  String _isbn;

  Book({
    required bookName,
    required isbn,
  })  : _bookName = bookName,
        _isbn = isbn;

  String get bookName => _bookName;
  set bookName(String newBookName) => _bookName = newBookName;

  String get isbn => _isbn;
  set isbn(String newISBN) => _isbn = newISBN;

  @override
  String toString() =>
      "Name of the book: $_bookName\nISBN for the book: $_isbn";
}

class Course {
  String _name;
  Book _book;
  Instructor _instructor;

  Course({
    required String name,
    required Book book,
    required Instructor instructor,
  })  : _name = name,
        _book = book,
        _instructor = instructor;

  String get name => _name;
  Book get book => _book;
  Instructor get instructor => _instructor;

  set name(String name) => _name = name;
  set book(Book book) => _book = book;
  set instructor(Instructor instructor) => _instructor = instructor;

  @override
  String toString() => "Name of Course: $_name\n$_book\n$_instructor";
}

// 결과
Name of Course: Dart Programming
Name of the book: Advanced Dart Programming
ISBN for the book: 123-456
Name of Instructor: John Doe

 

void main(List<String> args) {
  final a = A.fromConstructorBody(x: 1, y: 2, z: 3);
  print(a.x);
  print(a.y);
  print(a.z);

  print("==============================");

  final j = A.fromJson(json: {'x': 5, 'y': 10});
  print("x: ${j.x}, y: ${j.y}");

  print("==============================");

  final zx = A.zeroX(y: 100);
  print("x: ${zx.x}, y: ${zx.y}");
}

class A {
  int x, y, z;

  A({required this.x, required this.y, required this.z});

  A.fromInitList({required int x, required int y, required int z})
      : this.x = x,
        this.y = y,
        this.z = z;

  A.fromConstructorBody({required int x, required int y, required int z})
      : x = x,
        y = y,
        z = z {
    this.x = x;
    this.y = x;
    this.z = z + 1;
  }

  A.fromJson({required Map<String, int> json})
      : x = json['x']!,
        y = json['y']!,
        z = 0;

  A.zeroX({required int y}) : this(x: 0, y: y, z: 0);
}

// 결과
1
1
4
==============================
x: 5, y: 10
==============================
x: 0, y: 100

 

import 'dart:math';

void main(List<String> args) {
  final randomNegative = Point.random(isPositive: false);
  print("randomNegative: $randomNegative");
  final randomPositive = Point.random(isPositive: true);
  print("randomPositive: $randomPositive");
  final pointOrigin = Point.origin;
  print(pointOrigin);
}

class Point {
  final int x;
  final int y;

  static const Point origin = Point(x: 0, y: 0);
  const Point({required this.x, required this.y});

  factory Point.random({required bool isPositive}) {
    int minNegative = -99;
    int maxNegative = -1;
    int minPositive = 0;
    int maxPositive = 99;

    int randomNegative =
        minNegative + Random().nextInt(maxNegative - minNegative);
    int randomPositive =
        minPositive + Random().nextInt(maxPositive - minPositive);

    return isPositive
        ? Point(x: randomPositive, y: randomPositive)
        : Point(x: randomNegative, y: randomNegative);
  }

  @override
  String toString() => 'Point(x: $x, y: $y)';
}

// 결과
randomNegative: Point(x: -85, y: -85)
randomPositive: Point(x: 31, y: 31)
Point(x: 0, y: 0)

 

void main(List<String> args) {
  var ret1 = 1.luckyNumber;
  print(ret1);

  var ret2 = 10.add10();
  print(ret2);
}

extension IntegerExtension on int {
  int get luckyNumber => 7;
  int add10() => this + 10;
}

// 결과
7
20

 

void main(List<String> args) {
  Musician musician = Musician();
  // musician.playDrums();
  // musician.playGuitar();

  musician.perform();
}

class Performer {
  void perform() => print('Performs!');
}

mixin Guitarist {
  void playGuitar() => print('Playing the guitar.');
  void perform() => playGuitar();
}

mixin Durmmer {
  void playDrums() => print('Playing the drums.');
  void perform() => playDrums();
}

// with 순서에 따라 호출되는 메서드가 다르다.
class Musician extends Performer with Guitarist, Durmmer {}

// 결과
Playing the drums.

 

// mixins

void main(List<String> args) {}

abstract class Insect {
  crawl() {
    print('crawling.');
  }
}

abstract class AirborneInsect extends Insect {
  flutter() { // code duplicate
    print('fluttering.');
  }

  buzz() {
    print('buzzing annoyingly.');
  }
}

abstract class Bird {
  chirp() {
    print('chirp chirp.');
  }

  flutter() { // code duplicate
    print('fluttering.');
  }
}

class Swallow extends Bird {
  doSwallowThing() {
    chirp();
    flutter();
  }
}

class Mosquito extends AirborneInsect {
  doMosquitoThing() {
    crawl();
    flutter();
    buzz();
    print('sucking blood.');
  }
}

// 이런 계층 구조가 있을 때... 코드 중복이 필요하다면...

 

// mixins

void main(List<String> args) {
  Mosquito().doMosquitoThing();
  print('\n');
  Swallow().doSwallowThing();
  print('\n');
  Sparrow().peck(); 
}

mixin Fluttering {
  flutter() {
    print('fluttering.');
  }
}

abstract class Insect {
  crawl() {
    print('crawling.');
  }
}

abstract class AirborneInsect extends Insect with Fluttering {
  buzz() {
    print('buzzing annoyingly.');
  }
}

class Mosquito extends AirborneInsect {
  doMosquitoThing() {
    crawl();
    flutter();
    buzz();
    print('sucking blood.');
  }
}

abstract class Bird with Fluttering {
  chirp() {
    print('chirp chirp.');
  }
}

mixin Pecking on Bird {
  peck() {
    print('pecking.');
    chirp();
  }
}

class Swallow extends Bird with Fluttering {
  doSwallowThing() {
    chirp();
    flutter();
    print('eating a mosquito.');
  }
}

class Sparrow extends Bird with Pecking {}

class BlueJay extends Bird with Pecking {}

// 결과
crawling.
fluttering.
buzzing annoyingly.
sucking blood.


chirp chirp.
fluttering.
eating a mosquito.


pecking.
chirp chirp.

 

void main(List<String> args) {
  // final person = Person(name: 'John', age: 34, height: 1.85);
  // print(person.describe);

  final employee = Employee(
      name: 'John', age: 34, height: 1.85, taxCode: 'AB13', salary: 50000);
  employee.sayName;
  print("name: ${employee.name}, age: ${employee.age}, height: ${employee.height}");
}

class Person {
  final String name;
  final int age;
  final double height;

  Person({required this.name, required this.age, required this.height});

  String get describe =>
      "Hello, I'm $name. I'm $age years old, I'm $height meters tall.";
  void get sayName => print("Hello, I'm $name");
}

class Employee extends Person {
  String taxCode;
  int salary;

  Employee({
    required name,
    required int age,
    required double height,
    required this.taxCode,
    required this.salary,
  }) : super(name: name, age: age, height: height);
}

// 결과
Hello, I'm John
name: John, age: 34, height: 1.85

 

import 'dart:math';

// Abstract classes help us to program to interfaces, not implementations.

void main(List<String> args) {
  final square = Square(side: 10.0);
  // print("area: ${square.area()}");
  showArea(square);

  final circle = Circle(radius: 5.0);
  // print("area: ${circle.area()}");
  showArea(circle);

  print("==============================");

  print(square.calcArea);
  print(circle.calcArea);

}

void showArea(Shape shape) {
  print(shape.area());
}

// abstract classes define "interfaces"w
abstract class Shape {
  double area();
  double get calcArea; // computed proterties.
}

class Square implements Shape {
  final double side;
  Square({required this.side});

  @override
  double get calcArea => side * side;

  @override
  double area() => side * side;
}

class Circle implements Shape {
  final double radius;
  Circle({required this.radius});

  @override
  double get calcArea => radius * radius * pi;

  @override
  double area() => radius * radius * pi;
}

// 결과
100.0
78.53981633974483
==============================
100.0
78.53981633974483

 

void main(List<String> args) {
  final bmw = BMW(brand: 'BMW', model: 'X5');
  bmw.start();
  bmw.accelerate();
  bmw.stop();

  final honda = Honda(brand: 'Honda', model: 'Civic');
  honda.start();
  honda.accelerate();
  honda.stop();

/* 
Data Abstration: Data abstraction is one of the most essential and important
features of object-oriented programming.
Data abstraction refers to providing only essential information about the data
to the outside world hiding the backgeound details or implementation.
Consider a real-life example of a man driving a car. The man only knows that
pressing the accelerators will increase the speed of the car or
applying breaks will stop the car, but he does not know about how on pressing the
accelerator the speed is increasing, he does not know about the inner mechanism of
thr car or the implementation of the accelerator, brakes, etc in the car.
This is what abstraction is.
*/

  honda.passengers.forEach((element) {
    print(element);
  });
  honda.passengers.sort((a, b) => a.compareTo(b));
}
 
abstract class Car {
  String brand;
  String model;
  int _speed = 0;

  Car({
    required this.brand,
    required this.model,
  });

  start() {
    _speed = 20;
    print("$brand $model speed is $_speed");
  }

  stop() {
    _speed = 0;
    print("$brand $model speed is $_speed");
  }

  accelerate() {
    _speed += 50;
    print("$brand $model speed is $_speed");
  }
}

class Honda extends Car {
  final passengers = ['John', 'Emma', 'Danny', 'Juli'];
  Honda({required String brand, required String model})
      : super(brand: brand, model: model);
}

class BMW extends Car {
  BMW({required String brand, required String model})
      : super(brand: brand, model: model);
}

// 결과
BMW X5 speed is 20
BMW X5 speed is 70
BMW X5 speed is 0
Honda Civic speed is 20
Honda Civic speed is 70
Honda Civic speed is 0
John
Emma
Danny
Juli

 

void main(List<String> args) {
  
}

/*
Points to note

- By default, a constructor in a subclass calls the superclass's no-argument constructor.
- Parent class constructor is always called before child class constructor.
- If default constructor is missing in Parent class, then you must manually call one of
the constructors in super class.

*/

 

void main(List<String> args) {
  final b1 = Bar(idNum: 0020, nameStr: "'Bar from Foo'");
  b1.show();
}

/*
Points to note

- By default, a constructor in a subclass calls the superclass's no-argument constructor.
- Parent class constructor is always called before child class constructor.
- If default constructor is missing in Parent class, then you must manually call one of
the constructors in super class.

*/

abstract class Foo {
  final int idNum;
  final String nameStr;

  Foo({required this.idNum, required this.nameStr});

  show();
}

class Bar extends Foo {
  Bar({required super.idNum, required super.nameStr});

  @override
  show() {
    print("id: $idNum, name: $nameStr");
  }
}

// 결과
id: 20, name: 'Bar from Foo'

 

/*
Abstract Class and Method

= Abstract Method
- To make a method abstract, use semicolon (;) instead of method body.
- Abstract method can only exist with Abstract class.
- You need to override Abstract methods in sub-class.

= Abstract Class
- Use abstract keyword to declare Abstract Class.
- Abstract class can have Abstract Methods, Normal Methods and Instance Variables as well.
- The Abstract Calss cannot be instantiated, you cannot create objects.

*/

void main(List<String> args) {
  var rectangle = Rectangle(x: 1, y: 2);
  rectangle.draw();

  var circle = Circle(x: 3, y: 4); 
  circle.draw();
}

abstract class Shape {
  int x;
  int y;

  Shape({required this.x, required this.y});
  normalMethod() {} // not abstract method...

  draw();
}

class Rectangle extends Shape {
  Rectangle({required super.x, required super.y});

  @override
  draw() {
    print("Drawing Rectangle...");
  }
}

class Circle extends Shape {
  Circle({required super.x, required super.y});

  @override
  draw() {
    print("Drawing Circle...");
  }
}

// 결과
Drawing Rectangle...
Drawing Circle...

 

/*
Interface

- Dart does not have any special syntax to decalre INTERFACE.
- An INTERFACE in dart is a Normal Class.
- An INTERFACE is used when you need concrete implementation of all of its
functions within it's sub class.
- It is mandatory to override all methods in the implementing class.
- You can implement multiple classes but you cannot extend multiple classes during
Inheritance.
*/

void main(List<String> args) {
  var wm = WashingMachine();
  wm.volumeUp();
  wm.volumeDown();
}

class Remote {
  volumeUp() {
    print("Volume Up from Remote.");
  }

  volumeDown() {
    print("Volume Down from Remote.");
  }
}

class Television extends Remote {
  // 상속을 하면 수퍼 클래스의 메서드를 반드시 구현할 필요가 없다.
  // 상속을 하면 super.volumUp() 메서드를 호출할 수 있지만 이미 존재하기 때문에
  // 자기 참조이다.
}

class WashingMachine implements Remote {
  // 인터페이스로 구현을 하면 수퍼 클래스의 메서드를 반드시 구현해야 한다.
  // 인터페이스 구현을 하면, super.volumeUp() 등을 호출할 수 없다.
  // 여기서 인터페이스 구현은 메서드 이름만 상속 받는다는 걸 알 수 있다.
  @override
  volumeUp() {
    print("Volume Down in WashingMachine.");
  }

  @override
  volumeDown() {
    print("Volume Up in WashingMachine.");
  }
}

// 결과
Volume Down in WashingMachine.
Volume Up in WashingMachine.

 

/*

Inheritance promotes code reuse allows for the creation of hierarchical relationships
between classes. The subclass can access and utilitze the members (properties, methods) of
the superclass, including any puyblic or protected members. It can also add its own members
or override the behavior of the superclass members.
*/

void main(List<String> args) {
  final dog = Dog(name: 'Max', breed: 'Pit Bull');
  dog.eat();
  dog.sleep();
  dog.bark();

  print("");

  final cat = Cat(name: 'pow', breed: 'Persian');
  cat.eat();
  cat.sleep();
  cat.meow();

  // abstract class cannot be instantiated.
  // final animal = Animal(name: 'animal_name', breed: 'animal_breed');
}

abstract class Animal {
  String name;
  String breed;

  Animal({required this.name, required this.breed});

  eat() {
    print("$name is eating.");
  }

  sleep() {
    print("$name is sleeping.");
  }
}

class Dog extends Animal {
  Dog({
    required String name,
    required String breed,
  }) : super(name: name, breed: breed);

  bark() {
    print("$name is barking.");
  }
}

class Cat extends Animal {
  Cat({required String name, required String breed})
      : super(name: name, breed: breed);

  meow() {
    print("$name is meowing.");
  }
}

// 결과
Max is eating.
Max is sleeping.
Max is barking.

pow is eating.
pow is sleeping.
pow is meowing.

 

import 'dart:math' as math;

void main(List<String> args) {
  final rectangle = Rectangle(width: 2, height: 4);
  final square = Square(side: 4);
  final oval = Oval(majorRadius: 10, minorRadius: 5);
  final circle = Circle(radius: 7.5);

  // Polymorphism - using a classes children as that object.
  List<Shape> shapesList = [rectangle, square, oval, circle];
  shapesList.forEach((e) {
    e.getArea();
  });
}

// Subtyping - allow us to use a class as it's parents type.

abstract class Shape {
  double getArea();
}

class Rectangle implements Shape {
  double width, height;

  Rectangle({
    required this.width,
    required this.height,
  });

  @override
  double getArea() {
    print("Getting rectangle's area.");
    return width * height;
  }
}

class Square extends Rectangle {
  double side;

  Square({
    required this.side,
  }) : super(width: side, height: side);
}

class Oval implements Shape {
  double minorRadius;
  double majorRadius;

  Oval({
    required this.minorRadius,
    required this.majorRadius,
  });

  @override
  double getArea() {
    print("Getting oval's area.");
    return minorRadius * majorRadius * math.pi;
  }
}

class Circle extends Oval {
  double radius;

  Circle({
    required this.radius,
  }) : super(minorRadius: radius, majorRadius: radius);
}

// 결과
Getting rectangle's area.
Getting rectangle's area.
Getting oval's area.
Getting oval's area.

 

void main(List<String> args) {
  final school = School();
  school.addStudent(
      Student(name: '김서현', gender: 'male', age: 9, city: Cities.daejeon));
  school.addStudent(
      Student(name: '김이랑', gender: 'female', age: 11, city: Cities.busan));
  school.addStudent(
      Student(name: '신형우', gender: 'male', age: 11, city: Cities.seoul));
  school.addStudent(
      Student(name: '이학수', gender: 'male', age: 13, city: Cities.seoul));
  school.addStudent(
      Student(name: '이태수', gender: 'male', age: 14, city: Cities.busan));

  school.orderByAge();
  school.display();
}

enum Cities { seoul, busan, daejeon }

class School {
  List<Student> studentsList = [];

  void addStudent(Student student) {
    studentsList.add(student);
  }

  void orderByAge() {
    studentsList.sort((a, b) => a.age.compareTo(b.age));
  }

  void display() {
    for (Student s in studentsList) {
      print("name: ${s.name}(${s.age}), city: ${s.city.name}");
    }
  }
}

class Student {
  String name;
  String gender;
  int age;
  Cities city;

  Student({
    required this.name,
    required this.gender,
    required this.age,
    required this.city,
  });
}

// 결과
name: 김서현(9), city: daejeon
name: 김이랑(11), city: busan
name: 신형우(11), city: seoul
name: 이학수(13), city: seoul
name: 이태수(14), city: busan

 

void main(List<String> args) {
  final acc01 =
      BankAccount(accountName: '김희성', accountId: 123456789, balance: 0.0);
  acc01.deposit(1500.0);
  acc01.withdraw(300.0);
  acc01.display();

  print("");

  acc01.withdraw(100.0);
  acc01.display();

  print("");

  acc01.deposit(100.0);
  acc01.display();
}

enum AccountStatus {
  withdrawSuccess("인출성공"),
  withdrawFail("인출실패"),
  depositSuccess("입금성공");

  final String statusName;
  const AccountStatus(this.statusName);
}

class BankAccount {
  String accountName;
  int accountId;
  double balance = 0;
  AccountStatus accountStatus = AccountStatus.withdrawSuccess;

  BankAccount({
    required this.accountName,
    required this.accountId,
    required this.balance,
  });

  void deposit(double amount) {
    balance += amount;
    accountStatus = AccountStatus.depositSuccess;
  }

  void withdraw(double amount) {
    if (amount < 0) {
      accountStatus = AccountStatus.withdrawFail;
      throw Exception('Amount cannot be negative.');
    }

    if (amount > balance) {
      accountStatus = AccountStatus.withdrawFail;
      throw Exception('Balance is insufficient.');
    } else {
      balance -= amount;
      accountStatus = AccountStatus.withdrawSuccess;
    }
  }

  double get getBalance => balance;

  void display() {
    print('계좌번호: $accountId');
    print('예금주: $accountName');
    print('잔액: $balance');
    print('결과: ${accountStatus.statusName}');
  }
}

// 결과
계좌번호: 123456789
예금주: 김희성
잔액: 1200.0
결과: 인출성공

계좌번호: 123456789
예금주: 김희성
잔액: 1100.0
결과: 인출성공

계좌번호: 123456789
예금주: 김희성
잔액: 1200.0
결과: 입금성공

 

void main(List<String> args) {
  RentStore rs = RentStore();
  final c1 = Car(
    brand: 'Toyota',
    model: 'Camry',
    releaseYear: 2021,
    price: 30000.00,
  );

  final c2 =
      Car(brand: 'Honda', model: 'Civic', releaseYear: 2022, price: 25000.00);

  rs.addCar(c1);
  rs.addCar(c2);

  rs.bookCar(c1);
  rs.bookCar(c2);
  rs.listRented();

  print("==============================");
  print("");
  rs.returnCar(c1);
  rs.listRented();

  print("==============================");
  print("");
  rs.listAvailable();
}

class RentStore {
  static List<Car> allCar = [];
  static List<Car> bookedCar = [];

  void addCar(Car car) {
    allCar.add(car);
    car.book = false;
  }

  void bookCar(Car car) {
    if (allCar.isEmpty) {
      return;
    }

    bookedCar.add(car);
    allCar.remove(car);
    car.book = true;
  }

  void returnCar(Car car) {
    bookedCar.remove(car);
    allCar.add(car);
    car.book = false;
  }

  void listAvailable() {
    print("available cars: \n");
    for (Car c in allCar) {
      c.display();
    }
  }

  void listRented() {
    print("rented cars: \n");
    for (Car c in bookedCar) {
      c.display();
    }
  }
}

class Car {
  String brand;
  String model;
  int releaseYear;
  double price;
  bool isBooked = false;

  Car({
    required this.brand,
    required this.model,
    required this.releaseYear,
    required this.price,
  });

  bool get rentable => !isBooked;
  set book(bool booked) => isBooked = booked;

  void display() {
    print("Brand: $brand");
    print("Model: $model");
    print("Year: $releaseYear");
    print("Price: $price");
    print("Is booked? $isBooked");
    print("");
  }
}

// 결과
rented cars: 

Brand: Toyota
Model: Camry
Year: 2021
Price: 30000.0
Is booked? true

Brand: Honda
Model: Civic
Year: 2022
Price: 25000.0
Is booked? true

==============================

rented cars: 

Brand: Honda
Model: Civic
Year: 2022
Price: 25000.0
Is booked? true

==============================

available cars:
Brand: Toyota
Model: Camry
Year: 2021
Price: 30000.0
Is booked? false

 

void main(List<String> args) {
  final s1 = Student(name: '김희상', scores: [85, 90, 92, 88]);
  final s2 = Student(name: '최기원', scores: [76, 80, 72, 64]);

  s1.getGrade(s1.average);
  s1.display();

  print('');

  s2.getGrade(s2.average);
  s2.display();
}

enum Grades {
  A(90),
  B(80),
  C(70),
  D(60),
  F(50);

  final int score;
  const Grades(this.score);
}

class Student {
  String name;
  String grade = 'N/A';
  double scoreAverage = 0;
  List<int> scores = [];

  Student({
    required this.name,
    required this.scores,
  });

  double get average {
    if (scores.isEmpty) {
      throw Exception('Scores is empty. Try again.');
    }

    int total = 0;
    for (int s in scores) {
      total += s;
    }
    scoreAverage = total / scores.length;
    return scoreAverage;
  }

  String getGrade(double avg) {
    if (scoreAverage == 0) {
      throw Exception('Calculating the score average first.');
    }

    if (avg >= Grades.A.score) {
      grade = Grades.A.name;
    } else if (avg >= Grades.B.score) {
      grade = Grades.B.name;
    } else if (avg >= Grades.C.score) {
      grade = Grades.C.name;
    } else if (avg >= Grades.D.score) {
      grade = Grades.D.name;
    } else {
      grade = Grades.F.name;
    }
    return grade;
  }

  display() {
    if (scores.isEmpty) {
      throw Exception('Scores is empty. Try again.');
    }
    print('이름: $name');
    print("점수: $scores");
    print('평균 점수: $average');
    print('학점: $grade');
  }
}

// 결과
이름: 김희상
점수: [85, 90, 92, 88]
평균 점수: 88.75
학점: B

이름: 최기원
점수: [76, 80, 72, 64]
평균 점수: 73.0
학점: C

 

// ver 2

void main(List<String> args) {
  final s1 = Student(name: '김희상', scores: [85, 90, 92, 88]);
  final s2 = Student(name: '최기원', scores: [76, 80, 72, 64]);

  // s1.getGrade(s1.average);
  s1.display();

  print('');

  // s2.getGrade(s2.average);
  s2.display();
}

enum Grades {
  A(90),
  B(80),
  C(70),
  D(60),
  F(50);

  final int score;
  const Grades(this.score);
}

class Student {
  String name;
  String grade = 'N/A';
  double scoreAverage = 0;
  List<int> scores = [];

  Student({
    required this.name,
    required this.scores,
  });

  double get average {
    if (scores.isEmpty) {
      throw Exception('Scores is empty. Try again.');
    }

    int total = 0;
    for (int s in scores) {
      total += s;
    }

    return total / scores.length;
  }

  String getGrade(double avg) {
    if (scoreAverage == 0) {
      throw Exception('Calculating the score average first.');
    }

    if (avg >= Grades.A.score) {
      grade = Grades.A.name;
    } else if (avg >= Grades.B.score) {
      grade = Grades.B.name;
    } else if (avg >= Grades.C.score) {
      grade = Grades.C.name;
    } else if (avg >= Grades.D.score) {
      grade = Grades.D.name;
    } else {
      grade = Grades.F.name;
    }
    return grade;
  }

  display() {
    scoreAverage = average;
    getGrade(scoreAverage);
    if (scores.isEmpty) {
      throw Exception('Scores is empty. Try again.');
    }
    print('이름: $name');
    print("점수: $scores");
    print('평균 점수: $average');
    print('학점: $grade');
  }
}

// 결과
이름: 김희상
점수: [85, 90, 92, 88]
평균 점수: 88.75
학점: B

이름: 최기원
점수: [76, 80, 72, 64]
평균 점수: 73.0
학점: C

 

// How To Use Extension In Dart
void main(List<String> args) {
  String name = 'john';
  print(name.capitalize());
}

/*

Here we are extending the functionality of String class. 
We are adding a new method capitalize to the String class. 
We are using extension keyword to extend the functionality of String class.

*/

extension StringCaptitalize on String {
  String capitalize() {
    return this.substring(0, this.length).toUpperCase();
  }
}

// 결과
JOHN

 

void main(List<String> args) {
  // Cascade notation
  /*
    Dart provides a cascade operator (..) that 
    allows you to chain multiple assignments on the same object 
    without repeating the object name.
  */
  var p1 = Point()
    ..x = 10
    // It’s important to note that the semicolon (;) only appears on the last line.
    ..y = 11;

  print("x = ${p1.x}");
  print("y = ${p1.y}");
}

class Point {
  int x = 0;
  int y = 0;
}

// 결과
x = 10
y = 11

 

void main(List<String> args) {
  var p1 = Point();
  p1.move(10, 20);
  p1.show();
  print("After_____________");
  p1.reset();
  p1.show();

  // method chaining
  var p2 = Point();
  print("method chaining___");
  p2.move(100, 200).show();
  p2.move(100, 200).reset().show();
}

class Point {
  int x = 0;
  int y = 0;

  move(int x, int y) {
    this.x = x;
    this.y = y;
    return this;
  }

  reset() {
    this.x = 0;
    this.y = 0;
    return this;
  }

  show() {
    print("Point: ($x, $y)");
    return this;
  }
}

// 결과
Point: (10, 20)
After_____________
Point: (0, 0)
method chaining___
Point: (100, 200)
Point: (0, 0)

 

void main(List<String> args) {
  var p1 = Point(x: 1, y: 2);
  p1.show();
  var p2 = Point.reset();
  p2.show();
}

class Point {
  // ignore: prefer_final_fields
  int _x = 0;
  // ignore: prefer_final_fields
  int _y = 0;

  Point({required int x, required int y})
      : _x = x,
        _y = y;

  Point.reset() : this(x: 0, y: 0);

  show() {
    print("Point(x=$_x, y=$_y)");
  }
}

// 결과
Point(x=1, y=2)
Point(x=0, y=0)

 

import 'dart:math';

void main(List<String> args) {
  var c1 = Circle(radius: 0);
  print("Area: ${c1.area}");

  var c2 = Circle(radius: 1.5);
  print("Area: ${c2.area}");

  var c3 = Circle(radius: -1);
  print("Area: ${c3.area}");
}

/*
When you assign a value to the radius, Dart will automatically call the radius setter.
*/

class Circle {
  double _radius = 0;

  // Since the setter has a validation logic check,
  // you can reuse it in the constructor of the class.
  Circle({
    required double radius,
  }) {
    this.radius = radius; // <-- this is reusing setter...
  }

  set radius(double value) {
    if (value >= 0) {
      _radius = value;
    } else {
      throw Exception("Raidus should not be negative.");
    }
  }

  // Computed property
  //
  // By using a getter, you can define a computed property.
  // A computed property is not backed by a dedicated field
  // but instead is computed when called.

  double get radius => _radius;

  get area => radius * radius * pi;
}

// 결과
Area: 0.0
Area: 7.0685834705770345
Unhandled exception:
Exception: Raidus should not be negative.

 

// ignore_for_file: public_member_api_docs, sort_constructors_first
void main(List<String> args) {
  var p1 = Point(x: 1, y: 2);
  var p2 = Point(x: 1, y: 2);
  var result = (p1 == p2);
  print(result);
}

/*
Object equality

If you want two Point objects with the same x and y to be equal, 
you need to override the == operator in the Point class. For example:
*/

class Point {
  int x;
  int y;

  Point({
    required this.x,
    required this.y,
  });

  @override
  operator ==(o) => o is Point && o.x == x && o.y == y;
}

// 결과
true

 

void main(List<String> args) {
  var bird = Bird();
  bird.fly();

  var airplane = Airplane();
  airplane.fly();
}

abstract class LivingThing {}

class Human extends LivingThing {}

class LandAnimal extends LivingThing {}

mixin Flyable {
  void fly() {
    print("I can fly.");
  }
}

class Bird extends LivingThing with Flyable {}

class Airplane with Flyable {}

/*
Unlike a concrete class, you cannot create new objects 
from an abstract class. The main purpose of the abstract class 
is to allow other classes to inherit from it.

An abstract class may contain abstract properties and methods. 
Typically, an abstract class has abstract methods 
for its child classes to implement.

An interface is a contract between classes. 
Unlike other languages, Dart doesn’t have the interface keyword. 
Instead, all classes are implicit interfaces.

In practice, you often define an abstract class as an interface 
and implement the interface using other concrete classes.

추상 클래스가 있고 이를 상속하는 서브클래스 a, b, c가 있을 때
a, b에 공통으로 구현하고자 하는 메서드가 있고, c는 구현하고 싶지 않다면

방법1)
수퍼 클래스에 공통 메서드를 구현한다. 하지만 공통 메서드를 포함시키고 싶지
않은 서브 클래스도 상속을 받기 때문에 원하는 구조는 아니며, c 클래스에는
가짜로 구현해야 한다. 또, 이후 상속 받는 모든 서브 클래스가 불필요한
메서드를 상속 받는다.

방법2)
최상위 클래스 밑에 공통 메서드를 들고 있는 클래스를 만들고 a, b 클래스가
이를 상속한다. 하지만, 이 경우 다른 클래스 계층 구조에 있는 클래스는
이 중간 클래스를 상속 받지 못한다. 왜냐하면 Dart에서는 한 개의 클래스만
상속 받기 때문이다.

방법3)
인터페이스를 만들어 a, b 클래스가 해당 인터페이스를 구현한다. 좋은 방법이지만
a, b 클래스 각각 인터페이스를 구현하기 때문에 코드의 중복이 일어난다.

mixin 해결책
mixin 클래스를 만들어 끼워 사용하고자 하는 클래스만 with 하면 된다.
*/

// 결과
I can fly.
I can fly.

 

abstract class Character {
  final int? strength;
  final int? dexterity;
  final int? intelligence;

  Character({this.strength, this.dexterity, this.intelligence});

  double get strikingPower;
}

class Knight extends Character {
  Knight({
    required int strength,
    required int dexterity,
    required int intelligence,
  }) : super(
          strength: strength,
          dexterity: dexterity,
          intelligence: intelligence,
        );

  @override
  double get strikingPower => (strength! * 1.5) + dexterity! + intelligence!;
}

class Magician extends Character {
  Magician({
    required int strength,
    required int dexterity,
    required int intelligence,
  }) : super(
          strength: strength,
          dexterity: dexterity,
          intelligence: intelligence,
        );

  @override
  double get strikingPower => strength! + dexterity! + (intelligence! * 2);
}

class Thief extends Character {
  Thief({
    required int strength,
    required int dexterity,
    required int intelligence,
  }) : super(
            strength: strength,
            dexterity: dexterity,
            intelligence: intelligence);

  @override
  double get strikingPower => strength! + (dexterity! * 1.3) + intelligence!;
}

class AttackPoints<T extends Character> {
  List<T> character;
  AttackPoints({required this.character});

  double get strikingPower {
    double totalStrikingPower = 0;
    for (var char in character) {
      totalStrikingPower += char.strikingPower;
    }
    return totalStrikingPower;
  }
}

void main(List<String> args) {
  var k = Knight(strength: 90, dexterity: 60, intelligence: 30);
  var m = Magician(strength: 50, dexterity: 70, intelligence: 90);
  var t = Thief(strength: 70, dexterity: 50, intelligence: 80);

  var ap = AttackPoints(character: [k, m, t]);

  print("Knight's AP: ${k.strikingPower}");
  print("Magician's AP: ${m.strikingPower}");
  print("Thief's AP: ${t.strikingPower}");
  print("====================");
  print("Total Ap: ${ap.strikingPower}");
}

/*
추상 클래스, 구상 클래스 관계
제너릭 사용

str
dex
int

Striking power (공격력)

knight: str 90, dex, 60, int 30 (str * 1.5)
magician: str 50, dex 70, int 90 (int * 2)
thief: str 70, dex 50, int 80 (dex * 1.3)
*/

// 결과
Knight's AP: 225.0
Magician's AP: 300.0
Thief's AP: 215.0
====================
Total Ap: 740.0

 

/*
Introduction to Dart factory constructors

A generative constructor always returns a new instance of the class. 
Therefore, it doesn’t use the return keyword.

Unlike a generative constructor, a factory constructor uses 
the factory keyword and uses a return keyword 
to return an instance of the class.

The factory constructor may return an existing instance 
rather than a new instance. 

For example, it may return an instance from the cache. 
In addition, the factory constructor may return an instance of a subtype of the class.

Note: Use the factory keyword to define a factory constructor 
that returns an instance of the class (or its subclass).
*/

class Saver {
  final String saverName;
  bool _visible = true;
  static final _cache = <String, Saver>{};

  Saver._create({required this.saverName});

  factory Saver(String saverName) {
    return _cache.putIfAbsent(
        saverName, () => Saver._create(saverName: saverName));
  }

  set visible(bool visible) => _visible = visible;

  void show(String msg) {
    if (_visible) {
      print(msg);
    }
  }
}

void main(List<String> args) {
  var saver1 = Saver("firstSaver");
  saver1.show("First creation requested.");

  var saver2 = Saver("firstSaver");
  saver2.show("Second creation requested.");
  var result = identical(saver1, saver2)
      ? "First, Second are identical."
      : "First, Second are not identical.";
  print(result);

  print("");

  var saver3 = Saver("anotherSaver");
  saver3.show("Third creation requested.");
  result = identical(saver1, saver3)
      ? "First, Third instances are identical."
      : "First, Third are not identical.";
  print(result);
}


// 결과
First creation requested.
Second creation requested.
First, Second are identical.

Third creation requested.
First, Third are not identical.

 

/*
Mixins and Extensions
(extend the functionality of existing types/classes)

Mixin
  - Dart classes can extend only one class
  - Mixins solvethis problme -> add functionality and reuse code in
  your classes

Extensions
  - Add functionality to existing classes, without modifyingthem
  - Great when extending classes in the Dart/Flutter SDK or3rd party libraries

* Mixins lead to shallow class hierarchies (this is a good thing)
* Single inheritance (extends) leads to deep class hierarchies 
(making the code harder to maintain)

? In Dart, any class can be added as a mixin to another class
(using the with keyword)

+ Swimming, Breathing are verbs that represent an action.
+ When we create classes, we use nouns.

* Mixins have some drawbacks
  - Mixins can't have constructors (실제로 그렇기 때문에 믹스인을 인스턴스화 할 수 없다.)
  - Mixins can leat to name collisions
  

*/

void main(List<String> args) {
  final fo = FileObj();
  fo.log();

  final co = ConsoleObj();
  co.log();
}

// We can use mixins to share functionality inmultiple classes without code duplication.
mixin Logging {
  void log() => print("Logging..."); // 이 메서드 또한 mixin 으로 올려 클래스 별 선별 사용 가능
}

class OutStream {
  void show() => print("showing...");
}

class FileObj extends OutStream with Logging {}

class ConsoleObj extends OutStream with Logging {}

// 결과
2

 

void main(List<String> args) {
  final n = Numbers();
  n.printNum();
}

mixin Mixin1 {
  int i = 1;
}

mixin Mixin2 {
  int i = 2;
}

// Be careful about name collisions with mixins!
// - can be hard to spot them in big projects
// (and the compiler doesn't help)
//
// Should you use abstract classes & interfaces or mixins?
// -> It depends on your use case

class Numbers with Mixin1, Mixin2 {
  void printNum() {
    print(i); // Which i variable is this?
  }
}

// 결과
2

 

void main(List<String> args) {
  final sum1 = [1, 2, 3].sum();
  print(sum1);

  print("");

  final sum2 = [1.0, 2.0, 3.0].sumX();
  print(sum2);
}

extension IterableInt on Iterable<int> {
  int sum() => reduce((value, element) => value + element);
}

// eneric type constraints can be used with extensions AND Classes.
extension IterableX<T extends num> on Iterable<T> {
  T sumX() => reduce((value, element) => (value + element) as T);
}

// 결과
6
6.0

 

void main(List<String> args) {
  print(1.rangeTo(5));
}

extension RangeInt on int {
  List<int> rangeTo(int other) {
    if (other < this) {
      return []; // invalid handler
    }

    var _list = [this];
    for (var i = this + 1; i <= other; i++) {
      _list.add(i);
    }
    return _list;
  }
}

// 결과
[1, 2, 3, 4, 5]

 

void main(List<String> args) {}

class Point {
  final String name;
  int x;
  int y;

  static final Map<String, Point> _cache = {};

  Point(this.x, this.y, {required this.name});

  Point.zero()
      : name = 'zero',
        x = 0,
        y = 0;

  factory Point.fact(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name] as Point;
    } else {
      final point = Point(0, 0, name: name);
      _cache[name] = point;
      return point;
    }
  }

  void save() {
    _cache[name] = Point(x, y, name: name);
  }
}

 

abstract class RetailItem {
  double get price;
  set price(double price);
}

abstract class Labeled {
  String get label;
  set label(String label);
}

// I have to override all of the super class methods.
class CompactDisk implements RetailItem, Labeled {
  double _price;
  String _label;

  CompactDisk({required double price, required String label})
      : _price = price,
        _label = label;

  @override
  String get label => _label;

  @override
  double get price => _price;

  @override
  set label(String label) => _label = label;

  @override
  set price(double price) => _price = price;
}

 

/*

JSON example

var values = [
  {
    'firstName': 'Rack',
    'lastName': 'Jackson',
    'gender': 'man',
    'age': 24,
    'address': {
      "streetAddress": '126',
      'city': 'San jose',
      'state': 'CA',
      'postalCode': '394221'
    },
    'phoneNumber': [
      {'type': 'home', 'number': '7383627627'}
    ]
  }
];
*/

class Reading {
  double value;
  Reading({required double value}) : this.value = value;

  Reading.zero() : value = -999;

  @override
  String toString() {
    return '$value';
  }

  // return Reading
  factory Reading.fromServer({required Map<String, Object> server}) {
    final type = server['type'];
    final reading = server['reading'];

    switch (type) {
      case 'temp':
        final city = server['city'];
        if (city is String && reading is double) {
          return Temperature(city: city, reading: reading);
        } else {
          return Temperature.zero();
        }
      case 'pressure':
        final object = server['object'];
        if (object is String && reading is double) {
          return Pressure(objectTested: object, reading: reading);
        } else {
          return Pressure.zero();
        }

      default:
        return Reading.zero();
    }
  }
}

// Temperature is Reading
class Temperature extends Reading {
  String city;
  Temperature({required String city, required double reading})
      : this.city = city,
        super(value: reading);
  Temperature.zero()
      : city = '',
        super(value: -999);

  @override
  String toString() {
    return 'Temperature reading:\nReading: ${super.toString()}\nCity: $city \n';
  }
}

// Pressure is Reading
class Pressure extends Reading {
  String objectTested;
  Pressure({required String objectTested, required double reading})
      : this.objectTested = objectTested,
        super(value: reading);
  Pressure.zero()
      : objectTested = '',
        super(value: -999);
  @override
  String toString() {
    return 'Pressure reading:\nReading: ${super.toString()}\nObject Tested: $objectTested \n';
  }
}

void main() {
  var fromServer = [
    {
      'type': 'temp',
      'reading': 20.2,
      'city': 'Johannesburg',
    },
    {
      'type': 'pressure',
      'reading': 100.0,
      'object': 'Gass cilinder',
    },
    {
      'type': 'temp',
      'reading': 35.2,
      'city': 'New York',
    },
    {
      'type': 'pressure',
      'reading': 300.5,
      'object': 'Tyre',
    }
  ];

  var readings = <Reading>[];
  for (var item in fromServer) {
    readings.add(Reading.fromServer(server: item));
  }

  for (var reading in readings) {
    print(reading);
  }
}

/*
Factory keyword is used when implementing constructors that do not create
new instance of the exisiting class.

A factory constructors can return value from cache or from an instance of a sub-type.

Factory constructors are like static methods whose return type is the class itself.

Proper use case of factory constructor:
  Use factory constructor when creating a new instance of an existing class is too expensive.
  Creating only one instance of the class.
  For returning sub-class instance of the class instead of the class itself.
*/

// 결과
Temperature reading:
Reading: 20.2
City: Johannesburg 

Pressure reading:
Reading: 100.0
Object Tested: Gass cilinder 

Temperature reading:
Reading: 35.2
City: New York 

Pressure reading:
Reading: 300.5
Object Tested: Tyre

 

// ignore_for_file: constant_identifier_names

void main(List<String> args) {
  var theme = ThemeData();
  var theme2 = theme.copyWith(accentColor: Color.RED);
  print(theme2);
}

class ThemeData {
  final Color accentColor;
  final Color backgroundColor;
  final Color buttonColor;
  final Brightness brightness;

  ThemeData({
    this.accentColor = Color.BLUE,
    this.backgroundColor = Color.GREEN,
    this.buttonColor = Color.LIGHT_BLUE,
    this.brightness = Brightness.DARK,
  });

  ThemeData copyWith({
    Color? accentColor,
    Color? backgroundColor,
    Color? buttonColor,
    Brightness? brightness,
  }) =>
      ThemeData(
        accentColor: accentColor ?? this.accentColor,
        backgroundColor: backgroundColor ?? this.backgroundColor,
        buttonColor: buttonColor ?? this.buttonColor,
        brightness: brightness ?? this.brightness,
      );

  @override
  String toString() {
    return '''
    Accent Color: $accentColor
    Background Color: $backgroundColor
    Button Color: $buttonColor
    Brightness: $brightness
    ''';
  }
}

enum Color {
  RED,
  BLUE,
  GREEN,
  ORANGE,
  LIGHT_RED,
  LIGHT_BLUE,
  LIGHT_GREEN,
  LIGHT_ORANGE
}

enum Brightness { DARK, LIGHT }

// 결과
    Accent Color: Color.RED
    Background Color: Color.GREEN
    Button Color: Color.LIGHT_BLUE
    Brightness: Brightness.DARK

 

void main(List<String> args) {
  List<int> list = [1, 2, 3, 4, 5];
  print(list.average());
}

extension ListAverage on List<int> {
  double average() {
    double sum = 0;
    for (var l in this) {
      sum += l;
    }
    return (sum / length);
  }
}

// 결과
3.0
반응형

관련글 더보기

댓글 영역