01 Dart Genel Bakış ve İleri Nesne Yönelimli Programlama (PDF)
Document Details
Uploaded by Deleted User
Tags
Summary
Bu belge, Dart programlama dilinin temel kavramlarını, nesne yönelimli programlamayı ve değişkenleri açıklayan bir makaleyi içerir. Belge, Dart'ın kullanım alanlarını ve temel özelliklerini ele almaktadır.
Full Transcript
1 01-Dart Genel Bakış ve İleri Nesne Yönelimli Programlama Dart Nedir? Dart, Google tarafından geliştirilen açık kaynaklı, genel amaçlı bir programlama dilidir. Dart, web, sunucu ve mobil uygulamaların geliştirilmesi için tasarlanmıştır ve özellikle Flutter içinde...
1 01-Dart Genel Bakış ve İleri Nesne Yönelimli Programlama Dart Nedir? Dart, Google tarafından geliştirilen açık kaynaklı, genel amaçlı bir programlama dilidir. Dart, web, sunucu ve mobil uygulamaların geliştirilmesi için tasarlanmıştır ve özellikle Flutter içinde kullanılır. Flutter, mobil, web ve masaüstü platformları için yerel (natively compiled) uygulamalar geliştirmek için kullanılan bir Google kullanıcı arayüzü araç setidir ve Dart bu araç setinin ana dilidir. Dart'ın Temel Özellikleri: 1. Nesne Yönelimli (Object-Oriented): Dart, nesne yönelimli bir dildir. Bu, kodu sınıflar ve nesneler ile yapılandırdığı anlamına gelir. 2. Güçlü Tip Desteği: Dart, statik ve dinamik tür denetimini destekler. Geliştiriciler, türleri açıkça belirtebilir veya Dart'ın bu türleri otomatik olarak çıkarmasına izin verebilir. 3. Önceden Derleme (AOT) ve Anında Derleme (JIT): Dart, üretim yapıları için performansı artıran AOT derlemeyi ve geliştirme sırasında hızlı kod güncellemeleri sağlayan JIT derlemeyi destekler. 4. Asenkron Programlama: Dart, async/await sözdizimini kullanarak asenkron programlamayı yerleşik olarak destekler. Bu, özellikle sunucudan veri alma veya dosya okuma gibi işlemleri yönetmek için idealdir. 5. Çapraz Platform (Cross-Platform): Flutter ile Dart, Android, iOS, web ve masaüstü platformlarında çalışan çapraz platform uygulamaları geliştirmeye olanak tanır. Kullanım Alanları: Mobil Uygulamalar: Dart, özellikle Flutter ile güçlü ve performanslı mobil uygulamalar geliştirmek için kullanılır. Web Uygulamaları: Dart, JavaScript'e derlenerek modern tarayıcılarda çalışabilir. Sunucu Tarafı Uygulamalar: Dart, Dart Frog veya Shelf gibi paketlerle ölçeklenebilir sunucu tarafı uygulamalar geliştirmek için kullanılabilir. 2 Dart Ekosistemine Giriş Dart SDK ve Araçlar (DartPad, VS Code, CLI) Paketler ve Paket Yöneticisi (Packages and Package Manager - pub.dev) Proje Yapısı ve Büyük Projeleri Düzenlemede En İyi Uygulamalar (Project Structure and Best Practices for Organizing Large Projects) Değişkenler (Variables), Veri Türleri (Data Types) ve Kontrol Akışı (Control Flow) 1. Değişkenler (Variables) o var, final, const kullanarak değişken tanımlama ve türleri açıkça belirleme (int, double, String, vb.). void main() { // var kullanarak değişken tanımlama ve türü belirtme (Dart türü otomatik çıkarır) var name = "John"; // String var age = 25; // int var height = 1.85; // double // final kullanarak değişken tanımlama (değiştirilemez ama çalışma zamanında atanabilir) final String lastName = "Doe"; final int yearOfBirth = 1998; // const kullanarak sabit bir değer tanımlama (derleme zamanında bilinen sabitler) const double pi = 3.14159; const int maxItems = 100; // Çıktıları konsola yazdırma print('Name: $name, Age: $age, Height: $height'); print('Last Name: $lastName, Year of Birth: $yearOfBirth'); print('Pi: $pi, Max Items: $maxItems'); } 3 o Tür çıkarımı (Type Inference): Dart'ın var kullanırken otomatik olarak türleri nasıl çıkardığı. void main() { var number = 10; // int olarak çıkarım yapılır var text = "Hello"; // String olarak çıkarım yapılır var price = 99.99; // double olarak çıkarım yapılır print(number.runtimeType); // int print(text.runtimeType); // String print(price.runtimeType); // double } o Değişmez değişkenler (Immutable Variables): final ve const arasındaki fark. void main() { final DateTime currentTime = DateTime.now(); // çalışma zamanında atanır // const DateTime compileTimeError = DateTime.now(); // Hata verir çünkü const derleme zamanında sabit olmalı const double gravity = 9.81; // derleme zamanında sabit bir değer // gravity = 10; // Hata verir, const ile atanan değer değiştirilemez print('Current Time: $currentTime'); print('Gravity: $gravity'); } 4 o Değişkenlerin kapsamı (Scope of Variables): Blok kapsamı (Block Scope), yerel değişkenler (Local Variables) ve global değişkenler (Global Variables). // Global değişken tüm dosyalardan erişilelbilir. int globalVariable = 100; void main() { // main fonksiyonu içinde yerel bir değişken var localVariable = 50; if (true) { // Blok kapsamı var blockScopedVariable = 10; print('Block scoped variable: $blockScopedVariable'); // 10 } // Burada blockScopedVariable kullanılamaz, çünkü sadece blok içinde geçerli print('Local variable: $localVariable'); // 50 print('Global variable: $globalVariable'); // 100 // Diğer bir fonksiyon çağrısı anotherFunction(); } void anotherFunction() { // Global değişkene erişilebilir print('Global variable from anotherFunction: $globalVariable'); // 100 } 5 2. Veri Türleri (Data Types) o Temel veri türleri (Primitive Data Types): int, double, bool. void main() { // int: Tam sayı değerleri tutar int age = 25; // double: Ondalıklı sayı değerlerini tutar double height = 1.75; // String: Metin değerlerini tutar String name = "Ahmet"; //Referans tiptir, Dart ta primitif grup içinde sayılır. // bool: Mantıksal (true/false) değerleri tutar bool isStudent = true; print('Yaş: $age, Boy: $height, İsim: $name, Öğrenci mi: $isStudent'); } o Koleksiyonlar (Collections): List, Set, Map. void main() { // List (Sıralı bir koleksiyon) List fruits = ['Apple', 'Banana', 'Orange']; fruits.add('Mango'); print('Fruits: $fruits'); // Set (Tekil elemanları içeren sırasız koleksiyon) Set numbers = {1, 2, 3, 3}; // 3 tekrar eklenmez print('Numbers: $numbers'); // Map (Anahtar-değer çiftleri) Map studentGrades = {'Ali': 90, 'Veli': 85, 'Ahmet': 95}; print('Student Grades: $studentGrades'); } 6 o Tür kontrolü (Type Checking): is ve is! operatörleri ile değişken türü kontrolü. void main() { var value = 42; // Tür kontrolü if (value is int) { print('$value bir tam sayıdır.'); } if (value is! String) { print('$value bir metin değildir.'); } } o Tür dönüştürme (Type Casting): as kullanarak tür dönüştürme. void main() { Object value = '123'; // Tür kontrolü yaparak dönüştürme if (value is String) { int number = int.parse(value); // String'den int'e dönüştürme print('Dönüştürülen sayı: $number'); } // `as` ile tür dönüştürme (daha dikkatli kullanılmalıdır) dynamic dynamicValue = 42; var intValue = dynamicValue as int; print('Dinamik değerin tam sayı hali: $intValue'); } 7 o Dinamik tip (Dynamic Typing): dynamic ve Object? türlerini kullanma. void main() { // dynamic ile çalışma, Derleme zamanında tip kontrolü yapılmayan ve her türde veri tutabilen bir türdür dynamic value = 100; print('Value: $value, Type: ${value.runtimeType}'); // int türünde value = 'Hello'; print('Value: $value, Type: ${value.runtimeType}'); // String türünde // Object? ile çalışma, Herhangi bir türde değer tutabilen nullable (null alabilen) türdür. Object? data = null; // Null değeri alabilir data = 'Ahmet'; print('Object data: $data'); } o String interpolasyonu (String Interpolation): $ kullanarak string içerisine ifadeleri yerleştirme. void main() { String name = "Mehmet"; int age = 30; // Basit string interpolasyonu String message = "Merhaba, benim adım $name ve yaşım $age."; print(message); // İfadelerle string interpolasyonu String fullMessage = "Yaşımın iki katı: ${age * 2}"; print(fullMessage); } 8 3. Kontrol Akışı (Control Flow) o If-else ifadeleri (If-Else Statements): Temel sözdizimi ve iç içe koşullar (Nested Conditions). void checkAge(int age) { if (age >= 18) { print('You are an adult.'); } else { print('You are a minor.'); } } void main() { checkAge(20); // Output: You are an adult. checkAge(15); // Output: You are a minor. } o Switch-case: int, String ve enum türleriyle switch ifadelerinin kullanımı. Varsayılan durum (Default Case) ve geçiş engelleme (Fall-through Prevention). void describeNumber(int number) { switch (number) { case 1: print('One'); break; case 2: print('Two'); break; case 3: print('Three'); break; default: print('Unknown number'); } } void main() { describeNumber(2); // Output: Two describeNumber(5); // Output: Unknown number } 9 o Döngüler (Loops): ▪ for döngüsü (Standard for Loop ve for-in): Koleksiyonlar üzerinde yineleme. //For döngüsü void printNumbers(int n) { for (int i = 1; i 0) { print(start); start--; } } void printNumber(int n) { do { print(n); n--; } while (n > 0); } void main() { printNumber(3); // Output: 3 2 1 countDown(5); // Output: 5 4 3 2 1 } 10 ▪ break ve continue kullanımının döngülerdeki yeri. void skipEvenNumbers(int n) { for (int i = 1; i = 18 ? 'Adult' : 'Minor'; print(message); } void main() { checkAge(20); // Output: Adult checkAge(15); // Output: Minor } 11 o Null farkındalık operatörleri (Null-aware Operators): ??, ??= ve ?. kullanımı. // Null-Coalescing Operator ?? void printName(String? name) { print(name ?? 'Guest'); } void main() { printName(null); // Output: Guest printName('Alice'); // Output: Alice } //??= Operatörü: void setName(String? name) { name ??= 'Default Name'; print(name); } void main() { setName(null); // Output: Default Name setName('Alice'); // Output: Alice } // ?. Operatörü: class Person { String? name; void sayHello() { print('Hello, $name'); } } void main() { Person? person; person?.sayHello(); // Çıktı olmaz, çünkü person null person = Person()..name = 'Alice'; person?.sayHello(); // Output: Hello, Alice } 12 Fonksiyonlar (Functions): 1. Fonksiyon Tanımlama (Defining Functions) o Temel fonksiyon sözdizimi ve dönüş türleri (Return Types). // Bir tam sayı döndüren fonksiyon int addNumbers(int a, int b) { return a + b; } // Ana fonksiyonu çalıştıran ve ekrana bir şey yazdıran void türünde bir fonksiyon void printSum(int a, int b) { int sum = addNumbers(a, b); print('Sum: $sum'); } void main() { printSum(5, 10); // "Sum: 15" } o Birden fazla dönüş türü olan fonksiyonlar. // Enum kullanarak durumu tanımlıyoruz enum ResultType { success, error } // Birden fazla türde dönüş sağlayan fonksiyon Map processData(int value) { if (value > 0) { return { 'type': ResultType.success, 'data': 'Processed Value: ${value * 2}' }; } else { return { 'type': ResultType.error, 'message': 'Value must be greater than 0.' }; } } void main() { var result1 = processData(5); if (result1['type'] == ResultType.success) { 13 print(result1['data']); // "Processed Value: 10" } else { print(result1['message']); // Hata mesajı } var result2 = processData(-3); if (result2['type'] == ResultType.success) { print(result2['data']); } else { print(result2['message']); // "Value must be greater than 0." } } 14 2. Konumsal Parametreler (Positional Parameters) o Konumsal parametrelerle fonksiyon tanımlama ve çağırma. o Opsiyonel konumsal parametreler (Optional Positional Parameters) kullanımı []. o Varsayılan değerler (Default Values): Opsiyonel parametreler için varsayılan değer atama. void main() { // Fonksiyon çağrısı calculateArea(5.0, 10.0); // Farklı opsiyonel parametre kombinasyonlarıyla fonksiyon çağrıları greetUser('Ali'); greetUser('Veli', 'Merhaba!'); // Farklı varsayılan değer kombinasyonlarıyla fonksiyon çağrıları displayUserInfo('Ayşe'); displayUserInfo('Fatma', 30); displayUserInfo('Ahmet', 40, 'Mühendis'); } // Konumsal parametrelerle bir fonksiyon tanımlama void calculateArea(double width, double height) { double area = width * height; print('Dikdörtgenin Alanı: $area'); } // Opsiyonel konumsal parametrelerle bir fonksiyon tanımlama void greetUser(String name, [String greeting = 'Merhaba']) { print('$greeting, $name!'); } // Opsiyonel parametreler için varsayılan değerler atanmış bir fonksiyon tanımlama void displayUserInfo(String name, [int age = 25, String profession = 'Bilinmiyor']) { print('İsim: $name, Yaş: $age, Meslek: $profession'); } 15 3. İsimli Parametreler (Named Parameters) oİsimli parametrelerin {} ile tanımlanması. o Fonksiyon çağrılarında isimli parametrelerin netlik için kullanımı. o Zorunlu isimli parametreler (Required Named Parameters): required anahtar kelimesi ile zorunluluk ekleme. // İsimli Parametreler (Named Parameters) void main() { // İsimli parametrelerin {} ile tanımlanması displayUserInfo(name: 'Alice', age: 30); // Name: Alice, Age: 30 displayUserInfo(age: 25); // Name: Unknown, Age: 25 displayUserInfo(name: 'Bob'); // Name: Bob, Age: Unknown displayUserInfo(); // Name: Unknown, Age: Unknown // Zorunlu isimli parametreler (Required Named Parameters) displayProductInfo(name: 'Laptop', price: 1200); // displayProductInfo(price: 1200); // Bu satırda name eksik olduğu için hata alırız // varsayılan değer ile isimli parametreler displayUserWithDefaultValues(); // Name: Default Name, Age: 18 displayUserWithDefaultValues(name: 'Charlie'); // Name: Charlie, Age: 18 displayUserWithDefaultValues(age: 25); // Name: Default Name, Age: 25 // Zorunlu isimli parametreler ve varsayılan değerlerin kombinasyonu displayProductWithDefaults(name: 'Tablet'); displayProductWithDefaults(name: 'Phone', price: 999.99); } // İsimli Opsiyonel Parametrelerin {} ile Tanımlanması void displayUserInfo({String? name, int? age}) { print('Name: ${name ?? 'Unknown'}, Age: ${age ?? 'Unknown'}'); } //Zorunlu İsimli Parametreler (Required Named Parameters) void displayProductInfo({required String name, required double price}) { print('Product: $name, Price: \$$price'); } // Varsayılan Değer ile İsimli Parametreler void displayUserWithDefaultValues( {String name = 'Default Name', int age = 18}) { print('Name: $name, Age: $age'); } 16 //Zorunlu İsimli Parametreler ve Varsayılan Değerlerin Kombinasyonu void displayProductWithDefaults({required String name, double price = 0.0}) { print('Product: $name, Price: \$$price'); } 17 4. Anonymous (Lambda fonksiyonlar) // 4. Anonymous (Lambda fonksiyonlar) // Tek Satırlı Lambda Fonksiyonlar void main() { // Bir lambda fonksiyonu ile iki sayıyı toplama var add = (int a, int b) => a + b; print(add(3, 4)); // 7 // Bir listeyi filtrelemek için lambda kullanımı var numbers = [1, 2, 3, 4, 5, 6]; var evenNumbers = numbers.where((n) => n % 2 == 0).toList(); print(evenNumbers); // [2, 4, 6] } // Çok Satırlı Lambda Fonksiyonlar void main() { // Çok satırlı lambda kullanarak iki sayıyı toplama ve çarpma var calculate = (int a, int b) { int sum = a + b; int product = a * b; return 'Sum: $sum, Product: $product'; }; print(calculate(3, 4)); // Sum: 7, Product: 12 // Lambda fonksiyonu ile bir listeyi dönüştürme var numbers = [1, 2, 3, 4]; var transformedNumbers = numbers.map((n) { var result = n * n; print('Processing: $n'); return result; }).toList(); print(transformedNumbers); // Processing: 1, Processing: 2, Processing: 3, Processing: 4, [1, 4, 9, 16] } 18 5. Üst Düzey Fonksiyonlar (Higher-order Functions) o Fonksiyonları başka fonksiyonlara argüman olarak geçirme. // Fonksiyonları başka fonksiyonlara argüman olarak geçirme void main() { // Basit bir işlem fonksiyonu: Toplama int add(int a, int b) => a + b; // Basit bir işlem fonksiyonu: Çıkarma int subtract(int a, int b) => a - b; // Higher-order function: İşlem fonksiyonunu ve iki sayı alan bir fonksiyon int performOperation(int a, int b, int Function(int, int) operation) { return operation(a, b); } // `performOperation` fonksiyonuna `add` ve `subtract` fonksiyonlarını argüman olarak geçme int sum = performOperation(5, 3, add); print('Toplama Sonucu: $sum'); // Toplama Sonucu: 8 int difference = performOperation(5, 3, subtract); print('Çıkarma Sonucu: $difference'); // Çıkarma Sonucu: 2 // Daha kısa bir yazım için lambda (anonymous) fonksiyonu kullanarak bir işlem gerçekleştirelim int product = performOperation(5, 3, (a, b) => a * b); print('Çarpma Sonucu: $product'); // Çarpma Sonucu: 15 } 19 o Fonksiyonları geri döndürme. // Fonksiyonları geri döndürme void main() { // Higher-order function: Bir matematiksel işlem döndüren fonksiyon Function getOperation(String operation) { switch (operation) { case 'add': return (int a, int b) => a + b; // Toplama işlemini döndürür case 'subtract': return (int a, int b) => a - b; // Çıkarma işlemini döndürür case 'multiply': return (int a, int b) => a * b; // Çarpma işlemini döndürür default: return (int a, int b) => 0; // Geçersiz bir işlem girildiğinde varsayılan olarak 0 döndürür } } // `getOperation` fonksiyonundan bir fonksiyon alalım ve kullanalım var addOperation = getOperation('add'); print('Toplama: ${addOperation(4, 6)}'); // Toplama: 10 var multiplyOperation = getOperation('multiply'); print('Çarpma: ${multiplyOperation(4, 6)}'); // Çarpma: 24 // Varsayılan durumu kontrol edelim var unknownOperation = getOperation('unknown'); print('Bilinmeyen İşlem: ${unknownOperation(4, 6)}'); // Bilinmeyen İşlem: 0 } 20 6. Kapatmalar (Closures) o Dart'ta kapanışlar (Closures) nedir? o Kapanışlar ile değişken kapsamlarını yakalama örnekleri. // Dart'ta kapanışlar (Closures) nedir? // Closure'lar, bir fonksiyonun başka bir fonksiyon tarafından döndürüldüğü ve // dış ortamdan bir değişkeni yakalayarak kullanabildiği durumlardır. // Aşağıda basit bir closure örneği gösterilmektedir. Function sayacOlustur() { int sayac = 0; // Bu, closure tarafından yakalanacak olan değişken. return () { //geri döndürülecek dönecek metot sayac++; return sayac; }; } void main() { var sayac1 = sayacOlustur(); // sayacOlustur() fonksiyonunu çağır ve dönen closure'ı sakla. print(sayac1()); // 1 print(sayac1()); // 2 print(sayac1()); // 3 var sayac2 = sayacOlustur(); // Yeni bir closure oluştur. print(sayac2()); // 1 print(sayac2()); // 2 // Her closure kendi kapsamını ve kendi 'sayac' değişkenini kullanır. } 21 // Kapanışlar ile değişken kapsamlarını yakalama örnekleri // Closure, bulunduğu ortamın değişkenlerini yakalar ve bu değişkenlerle işlem yapabilir. // Aşağıdaki örnekte, 'carpimOlustur' fonksiyonu bir sabit sayı ile çarpma işlemi yapan // bir closure döndürüyor. Function carpimOlustur(int x) { return (int y) => x * y; // 'x' değişkeni closure tarafından yakalanır ve her çağrıda kullanılır. } void main() { var ikiIleCarp = carpimOlustur(2); // x = 2 olan bir closure oluşturuluyor. print(ikiIleCarp(3)); // 6 print(ikiIleCarp(5)); // 10 var ucIleCarp = carpimOlustur(3); // x = 3 olan yeni bir closure oluşturuluyor. print(ucIleCarp(4)); // 12 print(ucIleCarp(7)); // 21 // 'ikiIleCarp' ve 'ucIleCarp' closure'ları farklı değişkenleri yakalar ve farklı çarpma işlemleri yapar. } 22 Null Güvenliği (Null Safety) ve Null Olmayan Türlere Giriş (Non-nullable Types) 1. Null Güvenliği Nedir? (What is Null Safety?) o Null olabilen (Nullable - Type?) ve null olamayan (Non-nullable - Type) türler arasındaki fark. // Null güvenliği, null referans hatalarını önlemek için kullanılan bir mekanizmadır. // Örnek 1: Null Olabilen ve Null Olamayan Türler Arasındaki Fark void main() { // null olamayan bir String tanımı: String isim = "Ahmet"; print(isim); // Ahmet // null olabilen bir String tanımı: String? soyisim; print(soyisim); // null // null olabilen bir değişken atamadan kullanıldığında: if (soyisim != null) { print(soyisim.length); // Bu güvenli, çünkü soyisim null değil. } // Ancak direkt olarak null olabilen bir değişkene erişmeye çalışırsak hata alırız: // print(soyisim.length); // Bu kod hata verir, çünkü soyisim null olabilir. // Null olabilen bir değişkenin güvenli erişimi için null kontrol operatörleri kullanılabilir: print(soyisim ?.length); // Eğer soyisim null ise null döner; değilse uzunluğu döner. } // Örnek 2: Null Güvenliği ve Varsayılan Değer Kullanımı void kullaniciBilgisiYazdir(String? kullaniciAdi) { // Eğer kullaniciAdi null ise, varsayılan olarak "Kullanıcı Yok" yazdırılır. String ad = kullaniciAdi ?? "Kullanıcı Yok"; print(ad); } void main2() { kullaniciBilgisiYazdir(null); // Kullanıcı Yok kullaniciBilgisiYazdir("Mehmet"); // Mehmet } // Örnek 3: Non-Nullable Türlerin Kullanımı void kullaniciOlustur(String ad) { 23 // Bu fonksiyon null kabul etmez, çünkü 'ad' parametresi non-nullable türdür. print("Kullanıcı oluşturuldu: $ad"); } Null Farkındalık Operatörleri (Null-aware Operators) o Null doğrulama operatörü (!): Null olabilen bir değişkenin null olmadığını doğrulamak için kullanılır. o Null farkındalık erişim operatörü (?.): Null olabilir türlerin özelliklerine güvenli erişim. o Varsayılan değer operatörü (??): Eğer değişken null ise varsayılan bir değer sağlama. o Null farkındalık atama operatörü (??=): Değişkenin sadece null ise değer atama. // 2. Null Farkındalık Operatörleri (Null-aware Operators) // Null doğrulama operatörü (!): Null olabilen bir değişkenin null olmadığını doğrulamak için kullanılır. void nullDogulamaOperatoru() { String? isim = "Flutter"; String kesinIsim = isim!; // 'isim' değişkeninin null olmadığını garanti ederiz. print(kesinIsim); // Çıktı: Flutter } // Null farkındalık erişim operatörü (?.): Null olabilir türlerin özelliklerine güvenli erişim. void nullFarkindalikErisimOperatoru() { String? isim = null; int? uzunluk = isim?.length; // 'isim' null ise 'uzunluk' da null olur, hata oluşmaz. print(uzunluk); // Çıktı: null } // Varsayılan değer operatörü (??): Eğer değişken null ise varsayılan bir değer sağlama. void varsayilanDegerOperatoru() { String? mesaj = null; String sonuc = mesaj ?? "Varsayılan Mesaj"; // 'mesaj' null ise "Varsayılan Mesaj" kullanılır. print(sonuc); // Çıktı: Varsayılan Mesaj } 24 // Null farkındalık atama operatörü (??=): Değişkenin sadece null ise değer atama. void nullFarkindalikAtamaOperatoru() { int? sayi; sayi ??= 42; // 'sayi' null ise 42 değeri atanır. print(sayi); // Çıktı: 42 sayi ??= 100; // 'sayi' null olmadığı için bu atama gerçekleşmez. print(sayi); // Çıktı: 42 } void main() { nullDogulamaOperatoru(); nullFarkindalikErisimOperatoru(); varsayilanDegerOperatoru(); nullFarkindalikAtamaOperatoru(); } 25 Nesne Yönelimli Programlama (OOP) 1. Dart’ta Sınıflar ve Nesneler (Classes and Objects): Sınıf Tanımlama ve Oluşturma Sınıf Nedir? (What is a Class?) Sınıflardan Nesneler Oluşturma (Creating Objects from Classes) Özellikler (Properties - Instance Variables) ve Yöntemler (Methods - Instance Methods) Getter ve Setter'lar (Getters and Setters) Örnek Değişkenler (Instance Variables) ve Sınıf Değişkenleri (Class - Static Variables) arasındaki fark. Statik Yöntemler ve Değişkenler (Static Methods and Variables) Kapsülleme (Encapsulation): Özel Değişkenler (_ ile belirtilir) Dart'ın Nesneler için Belleği Nasıl Yönettiği (Memory Management) // Sınıf Nedir? (What is a Class?) // Sınıf, nesnelerin özelliklerini ve davranışlarını tanımlayan bir şablondur. // Sınıf, özellikler ve yöntemler içeren bir yapıdır ve nesnelerin nasıl davranacağını belirler. class Araba { // Özellikler (Properties) String marka; // Örnek değişken (Instance Variable) int hiz; // Örnek değişken (Instance Variable) String _renk; // Kapsülleme: Özel (Private) değişken // Sınıf Değişkeni (Class - Static Variable) // Tüm nesnelere ortak olan bir bilgiyi temsil eder. static int toplamArabaSayisi = 0; // Kurucu Metot (Constructor) // Nesne oluşturulurken çağrılır ve özelliklere başlangıç değerleri atar. Araba(this.marka, this.hiz,this._renk) { toplamArabaSayisi++; } // Yöntem (Method) // Sınıfın bir davranışını tanımlar, bu durumda aracın hızlanmasını sağlar. void hizlan(int artis) { 26 hiz += artis; print('$marka şimdi $hiz km/sa hızla gidiyor.'); } } void main() { // Nesne oluşturma (Creating Objects from Classes) Araba araba1 = Araba('Toyota', 50); araba1.hizlan(20); // Toyota şimdi 70 km/sa hızla gidiyor. // Getter ve Setter kullanımı (Getters and Setters) araba1.renk = 'Kırmızı'; print('Araba Renk: ${araba1.renk}'); // Araba Renk: Kırmızı // Statik değişken ve yöntem kullanımı (Static Variables and Methods) print('Toplam Araba Sayısı: ${Araba.toplamArabaSayisi}'); // Toplam Araba Sayısı: 1 Araba.kampanyaBilgisi(); // Bu kampanya tüm araçlar için geçerlidir. // Kapsülleme ve Bellek Yönetimi (Encapsulation and Memory Management) Personel personel = Personel('Ali'); personel.ad = 'Ahmet'; print('Personel Adı: ${personel.ad}'); // Personel Adı: Ahmet } 27 Getter ve Setter metotlar // Getter ve Setter'lar (Getters and Setters) // Özel değişkenlere dışarıdan güvenli erişim sağlar. String get renk => _renk; // Getter set renk(String yeniRenk) { if (yeniRenk.isNotEmpty) { _renk = yeniRenk; } } // Statik Yöntem (Static Method) // Sınıf ismi ile çağrılabilir, herhangi bir nesneye özgü değildir. static void kampanyaBilgisi() { print('Bu kampanya tüm araçlar için geçerlidir.'); } } // Kapsülleme (Encapsulation) örneği için Personel sınıfı class Personel { String _ad; // Özel değişken (Encapsulation) Personel(this._ad); // Getter String get ad => _ad; // Setter set ad(String yeniAd) { if (yeniAd.isNotEmpty) { _ad = yeniAd; } } } Kapsülleme // Kapsülleme (Encapsulation): Özel Değişkenler (_ ile belirtilir) // Sınıfın içinde bulunan özel (private) değişkenlere dışarıdan doğrudan erişimi engeller ve güvenli erişim sağlar. class Personel { String _ad; // Özel değişken (Encapsulation) Personel(this._ad); // Getter 28 String get ad => _ad; // Setter set ad(String yeniAd) { if (yeniAd.isNotEmpty) { _ad = yeniAd; } } } // Dart'ın Nesneler için Belleği Nasıl Yönettiği (Memory Management) // Dart, nesnelerin belleğini otomatik olarak yönetir ve kullanılmayan nesneleri çöp toplayıcı (Garbage Collector) ile temizler. class Kitap { String isim; Kitap(this.isim); // Kitap sınıfı, bellekte bir nesne oluşturur ve kullanılmadığında otomatik olarak temizlenir. } NOT: Sınıf, nesnelerin özelliklerini ve davranışlarını tanımlayan bir şablon iken, obje (nesne) bu sınıfın somut bir örneğidir ve bellekte gerçek bir varlık olarak bulunur. 29 Yapıcılar (Constructors): Varsayılan, İsimli ve Fabrika Yapıcılar (Factory Constructors) Varsayılan Yapıcı (Default Constructor) Netlik ve Özelleştirme için İsimli Yapıcılar (Named Constructors) o Birden Çok İsimli Yapıcı Tanımlama Fabrika Yapıcılar (Factory Constructors) o factory ile Nesne Önbellekleme veya Yeniden Kullanım o Fabrika Yapıcılar ile Normal Yapıcılar Arasındaki Farklar NOT: Fabrika ve Normal Yapıcılar Arasındaki Farklar Normal Yapıcı: Her çağrıldığında yeni bir nesne oluşturur. Kullanımı basit ve nesnelerin durumunu başlatmak için uygundur. Fabrika Yapıcı: Her zaman yeni bir nesne oluşturmak zorunda değildir. Daha önce oluşturulmuş bir nesneyi tekrar döndürebilir. Nesne oluştururken belirli mantıksal kontroller eklemek için kullanılır (örneğin, tek bir nesneye sahip olmayı sağlamak - Singleton Pattern). // Varsayılan Yapıcı (Default Constructor) // Sınıfın varsayılan bir yapıcıya sahip olması, nesne oluşturulmasını sağlar. class Araba { String? marka; String? renk; String? tip; // Varsayılan Yapıcı (return edemez) Araba(this.marka, this.renk); // İsimli Yapıcılar (Named Constructors) (return edemez) // Farklı durumlarda farklı başlangıç değerleri vermek için kullanılabilir. Araba.elektrikli(this.marka) { renk = 'Beyaz'; tip = 'Elektrikli'; } 30 // Fabrika Yapıcılar (Factory Constructors) // Belirli bir mantıkla nesne oluşturmak için kullanılır. (Return edebilir) factory Araba.fabrika(String tip) { if (tip == 'Spor') { return Araba('Ferrari', 'Kırmızı')..tip = 'Spor'; } else if (tip == 'Sedan') { return Araba('Mercedes', 'Siyah')..tip = 'Sedan'; } else { return Araba('Volkswagen', 'Gri')..tip = 'Standart'; } } } void main() { // Varsayılan Yapıcı (Default Constructor) kullanımı var varsayilanAraba = Araba('Toyota', 'Kırmızı'); print( 'Varsayılan Yapıcı: ${varsayilanAraba.marka}, Renk: ${varsayilanAraba.renk}'); // İsimli Yapıcı (Named Constructor) kullanımı var elektrikliAraba = Araba.elektrikli('Tesla'); print( 'İsimli Yapıcı: ${elektrikliAraba.marka}, Renk: ${elektrikliAraba.renk}'); // Fabrika Yapıcı (Factory Constructor) kullanımı var araba1 = Araba.fabrika('Spor'); var araba2 = Araba.fabrika('Sedan'); print('Fabrika Yapıcı 1: ${araba1.marka}, Tip: ${araba1.tip}'); print('Fabrika Yapıcı 2: ${araba2.marka}, Tip: ${araba2.tip}'); } 31 Kalıtım (Inheritance) ve Çok Biçimlilik (Polymorphism) Kalıtım Nedir? Neden Kullanılır? (What is Inheritance? Why Use It?) Bir Sınıfı Miras Almak için extends Kullanımı (Syntax for Inheriting a Class) Yöntemleri @override ile Ezme (Overriding Methods) Ana Sınıf Özelliklerine ve Yöntemlerine super ile Erişim (Accessing Parent Class) Soyut Sınıflar (Abstract Classes) ve Neden Kullanılır? Kalıtım ile Bileşim Arasındaki Fark (Inheritance vs Composition): Ne zaman hangisini kullanmalı? Çok Biçimlilik (Polymorphism): Alt sınıfların üst sınıfın yerine kullanılabilmesi. Dinamik Yöntem Çağrıları için Yöntem Ezme (Method Overriding) Uygulaması. Bir Sınıfı Miras Almak için extends Kullanımı // Bir Sınıfı Miras Almak için extends Kullanımı void main() { // Kalıtım ve Çok Biçimlilik Örneği // Kuş ve ördek nesneleri oluşturuldu ve çok biçimlilik kullanıldı Kus kus = Kus('Serçe'); kus.uc(); // Serçe kuşu uçuyor. Ordek ordek = Ordek('Yeşilbaş'); ordek.uc(); // Yeşilbaş ördek suyun üstünde uçar. // Çok Biçimlilik: Üst sınıf referansı kullanarak alt sınıf nesneleri Kus baskaBirOrdek = Ordek('Yaban Ördeği'); baskaBirOrdek.uc(); // Yaban Ördeği ördek suyun üstünde uçar. // super ile ana sınıfın özelliklerine erişim ordek.sesCikar(); // Yeşilbaş ördek vak vak diye ses çıkarıyor. } // Kalıtım, bir sınıfın başka bir sınıfın özellik ve yöntemlerini miras alarak tekrar kullanmasıdır. // Bu sayede kod tekrarından kaçınılır ve kod daha okunabilir olur. // Bir Sınıfı Miras Almak için extends Kullanımı (Syntax for Inheriting a Class) class Hayvan { String isim; Hayvan(this.isim); 32 void sesCikar() { print('$isim ses çıkarıyor.'); } } // Alt sınıf oluşturma ve extends kullanımı class Kus extends Hayvan { Kus(String isim) : super(isim); // Üst sınıfın yapıcısı çağrıldı void uc() { print('$isim kuşu uçuyor.'); } } Yöntemleri @override ile Ezme (Overriding Methods) class Kus extends Hayvan { Kus(String isim) : super(isim); // Üst sınıfın yapıcısı çağrıldı void uc() { print('$isim kuşu uçuyor.'); } } // Yöntemleri @override ile Ezme (Overriding Methods) class Ordek extends Kus { Ordek(String isim) : super(isim); // @override ile ana sınıftaki sesCikar yöntemini ezme @override void uc() { print('$isim ördek suyun üstünde uçar.'); } @override void sesCikar() { super.sesCikar(); // Ana sınıfın ses çıkarma metoduna erişim (super) print('$isim ördek vak vak diye ses çıkarıyor.'); } } // Soyut Sınıflar (Abstract Classes) ve Neden Kullanılır? // Soyut sınıflar, sadece alt sınıflar tarafından miras alınabilen ve nesne oluşturulamayan sınıflardır. 33 // Soyut sınıflar, belirli bir yapıyı zorunlu kılmak için kullanılır. abstract class Sekil { void alanHesapla(); // Soyut yöntem } class Dikdortgen extends Sekil { int en, boy; Dikdortgen(this.en, this.boy); @override void alanHesapla() { print('Dikdörtgenin alanı: ${en * boy}'); } } Kalıtım ile Bileşim Arasındaki Fark // Bileşim ise, bir sınıfın başka bir sınıfı "içermesidir". Bu, "has-a" ilişkisidir. class Motor { void calistir() { print('Motor çalıştı.'); } } class Araba { Motor motor = Motor(); // Bileşim (Composition) void hareketEt() { motor.calistir(); // Motoru kullanarak araba hareket eder. print('Araba hareket ediyor.'); } } // Çok Biçimlilik (Polymorphism): Alt sınıfların üst sınıfın yerine kullanılabilmesi // Çok biçimlilik, alt sınıf nesnelerinin üst sınıf referansıyla kullanılabilmesi anlamına gelir. // Bu sayede, ortak bir arayüzle farklı davranışlar sergilenebilir. void hayvaniUcurt(Hayvan hayvan) { if (hayvan is Kus) { hayvan.uc(); } else { print('${hayvan.isim} uçamaz.'); } } hayvaniUcurt (Ordek()) ; //çok biçimli çağırım desteklenir. 34 Soyut Sınıflar (Abstract Classes) ve Arayüzler (Interfaces) Soyut Sınıflar ve Soyut Yöntemler (Declaring Abstract Classes and Abstract Methods) Soyut Sınıflar Neden Kullanılır? (Why Use Abstract Classes?) Dart’ta Arayüzlerin Tanımlanması (Defining Interfaces) (implements kullanarak). Birden Fazla Arayüz Uygulaması (Implementing Multiple Interfaces) Dart’ta Soyut Sınıflar ile Arayüzler Arasındaki Farklar (Differences Between Abstract Classes and Interfaces). Soyut Sınıflar ve Arayüzler Ne Zaman Kullanılmalı? (When to Use Abstract Classes vs Interfaces) Arayüzlerle Yöntem Uygulamasının Zorunlu Hale Getirilmesi (Enforcing Method Implementation with Interfaces). NOT : Soyut Sınıflar: Hem somut hem de soyut üyeler içerebilir. Miras alınabilir (extends). Benzer özelliklere sahip sınıflar arasında ortak davranışları ve özellikleri miras almak için kullanılır. Bazı somut yöntemler de tanımlamak istiyorsanız kullanabilirsiniz. Arayüzler: Sadece soyut yöntemleri tanımlar, herhangi bir somut özellik içermez. implements ile kullanılır. Bir sınıfın birden fazla farklı davranışa (örneğin uçma, yüzme) sahip olmasını sağlamak için kullanılır. Sadece davranışları tanımlamak amacıyla kullanılır. Dart Dilinde interface anahtar kelimesi yoktur. Interface yaklaşımını soyut sınıflar ve içindeki gövdesiz metot imzalarıyla yapılır. // Soyut sınıf tanımlanıyor abstract class Animal { // Soyut bir yöntem (abstract method) tanımlandı void makeSound(); // Bu yöntem her alt sınıf tarafından uygulanmak zorundadır // Soyut sınıfın somut bir özelliği String color = "Tanımlanmamış"; // Alt sınıflar bu özelliği kullanabilir veya değiştirebilir 35 // Somut bir yöntem void sleep() { print( "Uyuyor..."); // Alt sınıflar bu yöntemi olduğu gibi kullanabilir veya override edebilir } } // Alt sınıflar soyut sınıfı miras alır ve soyut yöntemi uygulamak zorundadır class Dog extends Animal { @override void makeSound() { print("Hav Hav"); // Köpek sınıfı kendi sesini belirtti } } void main() { Dog dog = Dog(); dog.makeSound(); // Çıktı: Hav Hav dog.sleep(); // Çıktı: Uyuyor... print(dog.color); // Çıktı: Tanımlanmamış } 36 //Birden Fazla Arayüz Uygulaması abstract class Flyable { void fly(); // Arayüzde tanımlanan soyut bir yöntem } // Bird sınıfı Flyable arayüzünü uygular class Bird implements Flyable { @override void fly() { print("Kuş uçuyor"); } } abstract class Swimmable { void swim(); } // Duck sınıfı iki arayüzü birden uygular class Duck implements Flyable, Swimmable { @override void fly() { print("Ördek uçuyor"); } @override void swim() { print("Ördek yüzüyor"); } } void main() { Bird bird = Bird(); bird.fly(); // Çıktı: Kuş uçuyor Duck duck = Duck(); duck.fly(); // Çıktı: Ördek uçuyor duck.swim(); // Çıktı: Ördek yüzüyor } //Interface Çoklu Miras Alma (Multiple Inheritance) 37 Soyut Sınıf Polimorfizm // Soyut sınıf ve Polimorfizm abstract class Animal { void makeSound(); // Bu yöntem her alt sınıfta farklı şekilde uygulanır } // Farklı hayvan sınıfları soyut sınıfı miras alır class Dog extends Animal { @override void makeSound() { print("Hav Hav"); } } class Cat extends Animal { @override void makeSound() { print("Miyav"); } } // Polimorfizm kullanılarak farklı hayvan türlerini aynı referans üzerinden işleyelim void main() { List animals = [ Dog(), Cat() ]; // Hayvanlar listesi: Köpek ve Kedi nesneleri // Tüm hayvanlar üzerinde döngü ile ilerleyip ses çıkarma yöntemini çağırıyoruz for (var animal in animals) { animal.makeSound(); // Çıktı: Hav Hav (Dog için) // Çıktı: Miyav (Cat için) } } 38 Arayüz ve Polimorfizm // abstract class Drivable { void drive(); } class Car implements Drivable { @override void drive() { print("Araba sürülüyor"); } } class Bike implements Drivable { @override void drive() { print("Bisiklet sürülüyor"); } } // Polimorfizm kullanılarak farklı sürülebilir araçları aynı referans üzerinden işlemek void main() { List vehicles = [Car(), Bike()]; // Sürülebilir araçlar listesi for (var vehicle in vehicles) { vehicle.drive(); // Çıktı: Araba sürülüyor (Car için) // Çıktı: Bisiklet sürülüyor (Bike için) } } 39 Metotlar ve Polymorphizm // abstract class Animal { void makeSound(); // Soyut yöntem: Her alt sınıf bu yöntemi uygulamak zorunda } // Dog sınıfı Animal soyut sınıfından türetiliyor class Dog extends Animal { @override void makeSound() { print("Hav Hav"); // Köpeğe özgü ses çıkarma } } // Cat sınıfı Animal soyut sınıfından türetiliyor class Cat extends Animal { @override void makeSound() { print("Miyav"); // Kediye özgü ses çıkarma } } // Cow sınıfı Animal soyut sınıfından türetiliyor class Cow extends Animal { @override void makeSound() { print("Möööö"); // İneğe özgü ses çıkarma } } // Polimorfizmi gösteren bir metot void animalSound(Animal animal) { animal.makeSound(); // Gelen her türde Animal nesnesi için makeSound() çağrılır } void main() { Animal dog = Dog(); // Dog nesnesi Animal referansı üzerinden oluşturuluyor Animal cat = Cat(); // Cat nesnesi Animal referansı üzerinden oluşturuluyor Animal cow = Cow(); // Cow nesnesi Animal referansı üzerinden oluşturuluyor 40 // animalSound() metoduna farklı türlerde Animal nesneleri gönderiliyor animalSound(dog); // Çıktı: Hav Hav animalSound(cat); // Çıktı: Miyav animalSound(cow); // Çıktı: Möööö } Mixin’ler ve Bileşim (Composition) ile Kalıtım Üzerine (Mixins and Composition Over Inheritance) Mixin Nedir? Neden Kullanılır? (What is a Mixin? Why Use Mixins?) Mixin'lerin Tanımlanması ve Uygulanması (Defining and Applying Mixins) (with anahtar kelimesi ile). Mixin’ler için Kurallar (Rules for Mixins): Yapıcı (Constructor) içermemeleri. Bileşim Üzerine Kalıtım (Composition Over Inheritance): En İyi Uygulamalar (Best Practices). Birden Fazla Mixin’in Birleştirilmesi (Combining Multiple Mixins). Dart'ta Mixin'lerin Yaygın Kullanım Alanları (Common Use Cases for Mixins) – Paylaşılan Davranışlar. Kalıtım (Inheritance), Bileşim (Composition) ve Mixin'ler Arasındaki Farklar (Difference Between Inheritance, Composition, and Mixins). NOT: Mixin bir sınıf değildir (constructor) içeremezler, başka sınıflara ortak işlevsellik eklemek amacıyla kullanılan bir yapıdır. Mixin'ler kalıtıma alternatif olarak, bir sınıfın birden fazla özelliğe sahip olmasını sağlamak için kullanılır. Mixin'ler ile çoklu kalıtımın esnekliğinden faydalanmak mümkündür. Kalıtım (Inheritance): Bir sınıf, başka bir sınıfı genişletir ve tüm özelliklerini miras alır. extends kullanılır. Bileşim (Composition): Bir sınıf, başka bir sınıfın özelliklerine sahip olur fakat tam bir miras ilişkisi yoktur. Bir "parça" olarak kullanılır. Mixin'ler: Bir sınıfa ekstra işlevsellik eklemek için kullanılır. with anahtar kelimesi ile uygulanır ve çoklu kalıtımın sağladığı esnekliği sunar. 41 //mixin mixin Swimmable { void swim() { print("Yüzüyor..."); } } class Animal { void breathe() { print("Nefes alıyor..."); } } class Fish extends Animal with Swimmable {} void main() { Fish fish = Fish(); fish.breathe(); // Çıktı: Nefes alıyor... fish.swim(); // Çıktı: Yüzüyor... } // Çoklu mixin uygulama Farklı mixin'ler tanımlanıyor mixin Flyable { void fly() { print("Uçuyor..."); } } mixin Walkable { void walk() { print("Yürüyor..."); } } // Bird sınıfı Flyable ve Walkable mixin'lerini uygular class Bird with Flyable, Walkable {} void main() { Bird bird = Bird(); bird.fly(); // Çıktı: Uçuyor... bird.walk(); // Çıktı: Yürüyor... } 42 Kalıtım (Inheritance), Bileşim (Composition) ve Mixin'ler Arasındaki Farklar // class Engine { void start() { print("Motor çalışıyor..."); } } class Vehicle { void move() { print("Araç hareket ediyor..."); } } // Kalıtım: Car sınıfı Vehicle'ı genişletir class Car extends Vehicle {} // Bileşim: Motor özelliklerini kullanma class Motorcycle { final Engine engine = Engine(); void drive() { engine.start(); print("Motosiklet hareket ediyor..."); } } // Mixin: Ekstra özellik eklemek mixin Honkable { void honk() { print("Korna çalıyor..."); } } class Truck extends Vehicle with Honkable {} void main() { Car car = Car(); car.move(); // Çıktı: Araç hareket ediyor... Motorcycle motorcycle = Motorcycle(); motorcycle.drive(); // Çıktı: Motor çalışıyor... // Çıktı: Motosiklet hareket ediyor... Truck truck = Truck(); truck.move(); // Çıktı: Araç hareket ediyor... truck.honk(); // Çıktı: Korna çalıyor... } 43 this, super ve @override Anahtar Kelimeleri (this, super, and @override Keywords) this Anahtar Kelimesinin Rolü: Mevcut Nesneye Atıf (The Role of this in Referring to Current Instance). o this Anahtar Kelimesinin Açıkça Kullanıldığı Durumlar (When to Use this Explicitly). o this'i Yapıcı Parametrelerle Aynı İsimdeki Özellikler İçin Kullanma (Using this for Constructor Parameters with Same Names as Properties). super Anahtar Kelimesinin Kullanımı: Ana Sınıf Yapıcılarını ve Yöntemlerini Çağırma (Using super to Call Parent Class Constructors and Methods). @override Anahtar Kelimesi ile Ana Sınıf Yöntemlerini ve Özelliklerini Ezme (Overriding Parent Class Methods and Properties with @override). //this, super ve @override Anahtar Kelimeleri class Vehicle { String color; int speed; // Yapıcı (constructor) tanımlanıyor Vehicle(this.color, this.speed); // Sürüş yöntemi (drive method) void drive() { print("Araç $speed km/s hızla hareket ediyor."); } // Hızlanma yöntemi (accelerate method) void accelerate(int increment) { this.speed += increment; // this, mevcut sınıfın speed özelliğine erişmek için kullanılır print("Araç hızı $speed km/s'e yükseldi."); } } // Alt sınıf: Car, Vehicle sınıfından türetiliyor class Car extends Vehicle { String brand; // Yapıcı (constructor) ve super kullanımı Car(String color, int speed, this.brand) : super(color, speed); // @override ile drive yöntemini ezme (override) @override void drive() { super.drive(); // super kullanarak ana sınıfın drive() yöntemini çağırıyoruz 44 print("Araba markası: $brand."); // Ekstra davranış ekleniyor } // Benzersiz bir yöntem (unique method) ekliyoruz void honk() { print("$brand korna çalıyor: 'Bip Bip!'"); } } void main() { // Bir Car nesnesi oluşturuluyor ve this kullanımı yapıcıda gösteriliyor Car car = Car("Kırmızı", 120, "Toyota"); // this ile ilgili yöntemler ve özelliklere erişim car.accelerate(30); // Çıktı: Araç hızı 150 km/s'e yükseldi. // super ve @override kullanımıyla drive() yöntemi car.drive(); // Çıktı: Araç 150 km/s hızla hareket ediyor. (super ile ana sınıfın drive() yöntemi) // Çıktı: Araba markası: Toyota. (@override ile ek davranış) // Car sınıfına özgü bir yöntem car.honk(); // Çıktı: Toyota korna çalıyor: 'Bip Bip!' } 45 this ve super class Animal { String name; Animal(this.name) { print('$name doğdu.'); } } class Dog extends Animal { Dog(String name) : super(name) { print('$name havlıyor.'); } } class Shape { void draw() { print('Bir şekil çizildi.'); } } class Circle extends Shape { @override void draw() { super.draw(); print('Bir daire çizildi.'); } }