inblog logo
|
Uni
    Flutter

    3. Flutter 수업-dart 문법 예제 모음 part 2

    dart 문법
    홍윤's avatar
    홍윤
    Sep 26, 2024
    3. Flutter 수업-dart 문법 예제 모음 part 2
    Contents
    1. ex06.dart2. ex07.dart3. ex08.dart4. ex09.dart5. ex10.dart
     

    1. ex06.dart

    //클래스 안에 없으니까 기본함수 //add를 더 추가하고 싶으면 자바에선 오버로딩이라고 한다. //하지만 dart는 오버로딩을 지원하지않는다. void add(int n1, int n2) { print(n1 + n2); } //Type생략 가능 -> Type추론(좋은 코드는 아니다.) void add1(n1, n2) { print(n1 + n2); } // return을 받는 것은 무조건 타입을 지정해줘야한다. var은 리턴 되지 않는다. // Object타입은 받을 수 있다. dynamic은 가능하다. int minus(n1, n2) { return n1 = n2; } //원래코드 int multi(n1,n2)였다 하지만 function을 쓰려고하면 //익명함수로 바꿔야한다. [int multi](n1,n2) []를 지운다. Function f = (n1, n2) { n1 = 3; // return n1 * n2; }; //람다식 표현식 //n1 = 3; 이렇게 만들지 못한다. //이유는 람다식은 한줄로 만들어야하기 때문에 Function f2 = (n1, n2) => n1 * n2; void main() { add(1, 2); //add1("일", 2); int result = minus(1, 2); print(result); print(f(1, 2)); }
    notion image
    💡

    코드설명

    1. 함수 오버로딩

    • 오버로딩:
      • 여러 개의 함수가 같은 이름을 가지지만, 매개변수의 타입이나 수가 다른 경우를 말합니다.
      • Java에서는 오버로딩을 지원하지만, Dart는 기본적으로 함수 오버로딩을 지원하지 않습니다.
      • 대신, 매개변수의 기본값이나 옵셔널 매개변수를 사용하여 유사한 기능을 구현할 수 있습니다.

    2. 타입 추론과 명시적 타입 선언

    • 타입 추론:
      • add1 함수처럼 매개변수의 타입을 생략하면 Dart가 자동으로 타입을 추론합니다.
      • 이는 코드가 간결해지지만, 타입 안정성을 해칠 수 있습니다.
      • 따라서, 가능한 경우 명시적으로 타입을 선언하는 것이 좋습니다.
    • 명시적 타입 선언의 장점:
      • 코드의 가독성이 높아집니다.
      • 컴파일 시점에 타입 오류를 미리 잡을 수 있습니다.
      • 협업 시 다른 개발자들이 코드를 이해하기 쉽습니다.

    3. Function 타입과 익명 함수

    • Function 타입:
      • Dart에서 함수 자체를 변수로 취급할 수 있습니다.
      • Function 타입을 사용하면 다양한 형태의 함수를 변수에 할당할 수 있습니다.
    • 익명 함수와 람다식:
      • 익명 함수는 이름이 없는 함수로, 일회성으로 사용하거나 다른 함수의 인자로 전달할 때 유용합니다.
      • 람다식은 익명 함수를 간결하게 표현한 형태로, 단일 표현식만 포함할 수 있습니다.

    4. main 함수에서의 함수 호출

    • 함수 호출:
      • add, add1, minus, f, f2 등 다양한 함수를 호출하여 결과를 확인할 수 있습니다.
      • 주석 처리된 add1("일", 2);은 타입이 명시되지 않았기 때문에 실행 시 오류를 발생시킬 수 있습니다. 따라서 실제로는 주석을 유지하거나, 올바른 타입의 인수를 전달해야 합니다.

    주의사항

    1. minus 함수의 구현 오류:
        • 현재 return n1 = n2;는 n2의 값을 n1에 할당하고 그 값을 반환합니다. 만약 의도한 것이 뺄셈이라면 return n1 - n2;로 수정해야 합니다.
        • 예시:
          • int minus(n1, n2) { return n1 - n2; }
    1. add1 함수의 타입 문제:
        • add1 함수는 타입이 명시되지 않아, 다양한 타입의 인수를 받을 수 있지만, 실제로는 타입이 일치하지 않을 경우 오류가 발생할 수 있습니다.
        • 따라서, 가능하면 타입을 명시적으로 지정하는 것이 좋습니다.
        • 예시:
          • void add1(int n1, int n2) { print(n1 + n2); }
    1. 람다식과 다중 문장:
        • 람다식은 한 줄로 표현해야 하므로, 다중 문장을 포함할 수 없습니다. 만약 여러 문장을 포함해야 한다면 익명 함수 블록을 사용해야 합니다.
        • 예시:
          • Function f3 = (n1, n2) { n1 = 3; return n1 * n2; };

            결론

            Dart에서 함수 선언, 타입 추론, 익명 함수, 람다식 등의 개념을 잘 보여주고 있습니다. 다음과 같은 점들을 유의하면서 코드를 작성하면 더욱 안전하고 효율적인 Dart 프로그램을 작성할 수 있습니다.
          • 타입을 명시적으로 선언하여 코드의 가독성과 안정성을 높이세요.
          • 함수 오버로딩을 지원하지 않는 Dart의 특성을 이해하고, 대신 옵셔널 매개변수나 매개변수의 기본값을 활용하세요.
          • 익명 함수와 람다식을 적절히 사용하여 코드의 간결함을 유지하세요.
          • 함수 구현 시 논리 오류가 없는지 꼼꼼히 확인하세요.

    2. ex07.dart

    //함수 행위를 결정하지 못할 때 ()안에 function 행위(리스너)를 넣는다. //print 뽑는게 아니니까 이때는 익명함수를 쓰는게 좋다. void whenComeMother(Function beh) { beh(); } void off() { print("컴퓨터끄기"); } //리턴 안 할 거면 whenComeMother() => print("컴퓨터끄기") 이런식은 쓰지 말자! void main() { whenComeMother(() { print("컴퓨터 끄기"); }); } //new 생성자 생략가능 // dog a = dog();
    notion image
    💡

    코드설명

    고차 함수 (Higher-Order Function)

    • 정의:
      • 다른 함수를 인자로 받거나, 함수를 반환하는 함수를 고차 함수라고 합니다.
    • 장점:
      • 코드의 재사용성을 높이고, 유연한 동작을 구현할 수 있습니다.
      • 콜백(callback) 패턴을 통해 비동기 작업이나 이벤트 핸들링에 유용하게 사용됩니다.
    • 예제:
      • whenComeMother는 인자로 받은 함수를 실행하는 고차 함수입니다. 이를 통해 다양한 동작을 유연하게 정의할 수 있습니다.

    2. 익명 함수 (Anonymous Function)

    • 정의:
      • 이름이 없는 함수로, 주로 일회성으로 사용되거나 다른 함수의 인자로 전달될 때 사용됩니다.
    • 장점:
      • 코드의 간결성을 유지할 수 있습니다.
      • 특정 작업을 간단하게 정의할 수 있어, 코드의 가독성을 높입니다.
    • 예제:
      • main 함수에서 whenComeMother에 전달된 () { print("컴퓨터 끄기"); }는 익명 함수입니다.

    3. 람다식 (Lambda Expression)

    • 정의:
      • 단일 표현식으로 함수를 간결하게 정의하는 방법입니다. Dart에서는 화살표 함수(=>)를 사용하여 람다식을 표현할 수 있습니다.
    • 주의사항:
      • 람다식은 단일 표현식만을 포함할 수 있으며, 여러 문장을 포함하려면 익명 함수를 사용해야 합니다.
    • 예제:
      • 주신 코드에서는 람다식을 사용하지 않고 익명 함수를 사용하여 whenComeMother를 호출하고 있습니다. 이는 함수 내에서 여러 작업을 수행할 필요가 있을 때 적합합니다.

    4. 함수 타입 지정

    • Function 타입:
      • Dart에서 함수 자체를 변수로 취급할 수 있으며, Function 타입을 사용하여 함수를 인자로 받을 수 있습니다.
    • 타입 명시의 중요성:
      • 함수의 매개변수나 반환 타입을 명시적으로 지정하면, 코드의 안정성과 가독성이 향상됩니다.
      • 예를 들어, void whenComeMother(void Function() beh)와 같이 구체적으로 함수의 시그니처를 지정할 수 있습니다. 이는 잘못된 타입의 함수가 전달되는 것을 방지할 수 있습니다.
      • 요약

        Dart에서 고차 함수와 익명 함수를 활용하는 기본적인 예제입니다. 이를 통해 다른 함수의 동작을 유연하게 정의하고, 필요에 따라 다양한 행위를 전달할 수 있습니다. 다음과 같은 점들을 유의하면서 코드를 작성하면 더욱 안전하고 효율적인 Dart 프로그램을 작성할 수 있습니다.
      • 고차 함수를 활용하여 코드의 재사용성과 유연성을 높이세요.
      • 익명 함수와 람다식을 적절히 사용하여 코드의 간결함을 유지하세요.
      • 함수 타입을 명시적으로 지정하여 코드의 안정성과 가독성을 향상시키세요.
      • 타입 안전성을 유지하기 위해 함수의 매개변수와 반환 타입을 구체적으로 정의하세요.

    3. ex08.dart

    class Cat { String name; int age; String color; int thirsty; //이게 쓸 일이 많다. Cat(this.name, this.age, this.color, this.thirsty); } class Dog { String name; int age; String color; int thirsty; //이니셜라이즈 키워드 //한줄 요약시 Dog(String name, int age, String color, int thirsty) : this.name = name, this.age = age, this.color = color, this.thirsty = thirsty; } void main() { Dog("", 1, "", 1); }
    💡
     

    생성자(Constructor)의 두 가지 방식

    • 생성자 파라미터 이니셜라이저 (Cat 클래스):
      • Cat(this.name, this.age, this.color, this.thirsty);
      • 장점:
        • 코드가 간결합니다.
        • 클래스의 속성과 생성자 파라미터 간의 매핑이 명확합니다.
      • 단점:
        • 초기화 로직이 복잡할 경우 사용하기 어렵습니다.
    • 이니셜라이저 리스트 (Dog 클래스):
      • dart코드 복사Dog(String name, int age, String color, int thirsty) : this.name = name, this.age = age, this.color = color, this.thirsty = thirsty;
      • 장점:
        • 초기화 로직을 자유롭게 작성할 수 있습니다.
        • 상위 클래스의 생성자를 호출할 때 유용합니다.
      • 단점:
        • 코드가 다소 길어질 수 있습니다.

    2. 생성자 오버로딩

    • 설명:
      • 오버로딩은 같은 이름의 함수를 여러 번 정의하여, 매개변수의 타입이나 수에 따라 다른 동작을 수행하게 하는 기법입니다.
      • Java에서는 생성자 오버로딩을 지원하지만, Dart는 기본적으로 생성자 오버로딩을 지원하지 않습니다.
    • 대안:
      • 명명된 생성자: 같은 클래스 내에서 여러 생성자를 정의할 수 있습니다.
        • class Dog { String name; int age; String color; int thirsty; Dog(this.name, this.age, this.color, this.thirsty); // 명명된 생성자 Dog.brownDog(String name, int age) : this(name, age, "갈색", 5); } void main() { Dog dog1 = Dog("멍멍이", 3, "갈색", 7); Dog dog2 = Dog.brownDog("브라운", 2); }
      • 옵셔널 매개변수: 생성자의 매개변수를 옵션으로 만들어 유연하게 초기화할 수 있습니다.
        • class Dog { String name; int age; String color; int thirsty; Dog(this.name, this.age, {this.color = "검은색", this.thirsty = 5}); } void main() { Dog dog1 = Dog("멍멍이", 3); Dog dog2 = Dog("브라운", 2, color: "갈색", thirsty: 7); }

          요약

          Dart에서 클래스와 생성자를 정의하는 두 가지 방식을 잘 보여주고 있습니다. Cat 클래스는 생성자 파라미터 이니셜라이저를 사용하여 간결하게 속성을 초기화하고, Dog 클래스는 이니셜라이저 리스트를 사용하여 보다 유연하게 속성을 초기화하고 있습니다.
          또한, Dart에서는 new 키워드를 생략할 수 있으며, 생성자 오버로딩을 직접 지원하지 않지만 명명된 생성자나 옵셔널 매개변수를 통해 유사한 기능을 구현할 수 있습니다.
        • Tip:
          • 생성자에서 타입을 명시적으로 지정하는 습관을 들이면 코드의 가독성과 안정성을 높일 수 있습니다.
          • 명명된 생성자나 옵셔널 매개변수를 활용하여 클래스의 인스턴스를 다양한 방식으로 초기화할 수 있습니다.

    4. ex09.dart

    class Dog { int age; String name; //오류가 사라지는 이유는 값이 무조건 들어가는 상황이기 때문에 오류가 나지 않는다. //Dog(this.age, this.name) //빌더 패턴이랑 비슷한 경우이다. Dog({required this.age, required this.name}); } //1. ?타입이 있고 //2. {} 선택적 매개변수를 받을 수 있다. class Cat { int? age; String? name; //값을 초기화 하고 싶은때는 //선택적 매개변수에게만 디폴트값을 설정 할 수 있다. Cat({this.age, this.name = "토토"}); void cry() { print("야옹"); } //값을 바로 보고싶으면 오버라이딩을 해주면 된다. //생성자 자동완성 키 찾아보기! String toString() { return "age: ${age}, name : ${name}"; } } //선택적 매개변수의 뜻은 넣어도 되고 안 넣어도 된다. //age:1 이런식으로 생략이 가능하다. 순서도 바꿔도 상관없다. //자바의 빌더패턴이 필요가 없다. 빌더패턴은 장점은 순서가 상관없다. 실수도 없다. //required를 쓰면 name,age를 반드시 받아야한다.!!!! //Cat의 경우는 하나만 써도 받을 수 있다. void main() { Dog d = Dog(name: "토토", age: 10); Cat c = Cat(age: 10); //케스케이드연산자 Cat c1 = Cat(age: 10)..cry(); print("-----------------------------------"); print(d); print("-----------------------------------"); print(c); print("-----------------------------------"); print(c1); }
    notion image
    💡

    코드설명

    1.명명된 매개변수(named parameters)와 필수 매개변수(required parameters)

    • 명명된 매개변수(named parameters):
      • 매개변수를 중괄호 {} 안에 정의하면, 객체를 생성할 때 매개변수의 이름을 명시적으로 지정하여 값을 전달할 수 있습니다.
      • 예: Dog(name: "토토", age: 10);
      • 장점:
        • 매개변수의 순서에 구애받지 않습니다.
        • 매개변수의 의미를 명확하게 전달할 수 있습니다.
    • 필수 매개변수(required parameters):
      • required 키워드를 사용하면, 해당 매개변수를 반드시 제공해야 합니다.
      • 이는 객체 생성 시 필수적인 속성을 빠뜨리지 않도록 도와줍니다.
      • 예: Dog({required this.age, required this.name});

    2. 널 허용 타입(null safety)

    • Dart는 **널 안전성(null safety)**을 지원하여, 변수에 null 값을 허용할지 여부를 명확하게 지정할 수 있습니다.
    • 널 허용 타입(nullable types):
      • int?, String?와 같이 ?를 붙여서 선언하면, 해당 변수는 null 값을 가질 수 있습니다.
      • 예: int? age;, String? name;
    • 널 허용하지 않는 타입(non-nullable types):
      • int, String과 같이 ? 없이 선언하면, 해당 변수는 null 값을 가질 수 없습니다.
      • 예: int age;, String name;
    • 기본값 설정:
      • 선택적 매개변수에 기본값을 설정하면, 값을 제공하지 않아도 기본값이 자동으로 할당됩니다.
      • 예: Cat({this.age, this.name = "토토"});

    3. 오버라이딩(overriding)

    • toString 메소드 오버라이딩:
      • toString 메소드를 오버라이딩하면, 객체를 문자열로 표현할 때 원하는 형식으로 출력할 수 있습니다.
      • 예:
        • @override String toString() { return "age: ${age}, name: ${name}"; }
      • 이는 디버깅 시 객체의 상태를 쉽게 확인할 수 있게 도와줍니다.

    4. 케스케이드 연산자(cascade operator)

    • 정의:
      • 케스케이드 연산자(..)는 객체를 생성한 후, 해당 객체의 메소드를 연속적으로 호출할 때 사용됩니다.
    • 사용 예:
      • Cat c1 = Cat(age: 10)..cry();
      • c1 객체를 생성한 후, 바로 cry 메소드를 호출합니다.
    • 장점:
      • 객체를 생성하면서 여러 메소드를 연속적으로 호출할 수 있어 코드가 간결해집니다.
    • 주의사항:
      • 메소드 체이닝을 사용할 때, 객체가 올바르게 초기화되었는지 확인해야 합니다.

    5. 빌더 패턴과 Dart의 대안

    • 빌더 패턴(builder pattern):
      • Java 등에서 객체의 복잡한 생성 과정을 단순화하고, 매개변수의 순서에 구애받지 않도록 도와주는 디자인 패턴입니다.
    • Dart의 대안:
      • Dart에서는 명명된 매개변수(named parameters)와 필수 매개변수(required parameters)를 사용하여 빌더 패턴과 유사한 기능을 구현할 수 있습니다.
      • 또한, 선택적 매개변수(optional parameters)와 기본값 설정을 통해 유연한 객체 생성을 지원합니다.
      • 요약

        Dart에서 클래스와 생성자를 정의하는 다양한 방법을 잘 보여주고 있습니다. **명명된 매개변수(named parameters)**와 **필수 매개변수(required parameters)**를 사용하여 객체 생성 시 매개변수의 순서에 구애받지 않고, 필요한 값을 명확하게 제공할 수 있습니다. 또한, **널 허용 타입(null safety)**과 기본값 설정을 통해 더욱 유연하고 안전한 코드를 작성할 수 있습니다.
      • 명명된 매개변수와 필수 매개변수를 적절히 사용하여 객체 생성을 명확하고 안전하게 관리하세요.
      • 널 허용 타입을 통해 필요한 경우에만 null을 허용하고, 그렇지 않은 경우에는 필수 값을 제공하도록 설계하세요.
      • toString 메소드 오버라이딩을 활용하여 객체의 상태를 쉽게 출력하고, 디버깅 시 유용하게 사용하세요.
      • 케스케이드 연산자를 사용하여 객체 생성과 메소드 호출을 간결하게 표현하세요.
      • 명명된 생성자나 빌더 패턴을 활용하여 복잡한 객체 생성 과정을 효율적으로 관리하세요.

    5. ex10.dart

    // null 처리방법 int? findById(int id) { return id == 1 ? 1 : null; } main() { //위험한코드 1 :특정한 값이 들어오면 터질 수 있기때문에 "!" 확실하지 않을때 쓰면 안된다. int r1 = findById(1)!; print(r1); print("-------------------------------------------------"); //위험한 코드 2: null 대체연산자(Orelse) int r2 = findById(5) ?? 0; print(r2); print("-------------------------------------------------"); // findById(5).toDouble(); 이건 되지 않는다. null일 수 있기 때문에 //findById(5)!.toDouble(); //이 함수는 null이면 실행하지 말구 null 아니면 실행이 되게한다. //이렇게 한 줄로 연산자를 쓸 수 있다. double? r3 = findById(5)?.toDouble() ?? 0; print(r3); //null 이면 0.0으로 받는다. //print(r3 ?? 0.0); }
    notion image
    💡

    코드설명

    1. findById 함수
        • 역할: 주어진 id가 1이면 1을 반환하고, 그렇지 않으면 null을 반환합니다.
        • 반환 타입: int?는 int 또는 null을 반환할 수 있음을 의미합니다.
    1. main 함수 내 코드
        • r1 선언 및 출력
          • int r1 = findById(1)!; print(r1);
          • 설명: findById(1)은 1을 반환하므로 ! 연산자를 사용해 null이 아님을 확신하고 int 타입으로 변환합니다.
          • 주의: 만약 findById가 null을 반환하면 런타임 오류가 발생합니다.
          • 출력: 1
        • r2 선언 및 출력
          • int r2 = findById(5) ?? 0; print(r2);
          • 설명: findById(5)는 null을 반환하므로, ?? 연산자를 사용해 null일 경우 기본값 0을 할당합니다.
          • 출력: 0
        • r3 선언 및 출력
          • double? r3 = findById(5)?.toDouble() ?? 0; print(r3);
          • 설명:
            • findById(5)가 null이면 ?. 연산자로 인해 toDouble()이 호출되지 않고 null이 됩니다.
            • ?? 연산자를 통해 null일 경우 기본값 0을 할당합니다.
          • 출력: 0

    핵심 포인트

    • 널 단언 연산자(!):
      • 변수나 함수가 null이 아님을 확신할 때 사용합니다.
      • 확신할 수 없을 경우 사용하면 런타임 오류가 발생할 수 있습니다.
    • 널 병합 연산자(??):
      • 왼쪽 값이 null일 경우 오른쪽 값을 반환합니다.
      • 안전하게 기본값을 제공할 때 유용합니다.
    • 널 안전 호출 연산자(?.):
      • 객체가 null이 아닐 때만 메소드를 호출합니다.
      • null일 경우 null을 반환하여 오류를 방지합니다.
      • 요약

        Dart에서 null을 안전하게 처리하는 여러 방법을 활용할 수 있습니다. ! 연산자는 신중하게 사용하고, 가능한 ??나 ?. 연산자를 사용하여 안전성을 높이는 것이 좋습니다.
    Share article

    Uni

    RSS·Powered by Inblog