Merhabalar.

Dart’ın bir diğer önemli konularından biri olan Generic kavramını anlatmaya çalışacağım bu yazımda.

Türkçe’ye jenerik ya da genel türler şeklinde çevrilen kavram tür bağımsız ifadeleri açıklar. Yazmış olduğumuz sınıfların, fonksiyonların vb., belli bir türe göre değil, her türe göre çalışmasını sağlamaktadır. Örneğin String, int, Araba (özel sınıf) vb. her şey birer jenerik tür olabilir. Bu türü ihtiyaca göre siz belirlersiniz. Ama belirlemek zorunda değilsiniz. Tür belirlemezseniz dart dili otomatik olarak türü dynamic olarak belirler.

Aslında program yazarken jenerik tipleri farkına varmadan kullanırız. Jenerikler genelde liste yapılarında kullanılır. Dart dilinde kullanılan List<T> sınıfı,  jenerik tür alan sınıftır. Siz sadece öğrenci adlarını içerecek String türünde bir liste tanımlarsınız. O liste artık size isim listesi olarak hizmet eder. Araya sayı veya başka bir ifade ekleyemezsiniz. Çünkü türünü belirlediğiniz için derleme anında derleyici size hata verecektir. Bu size tip güvenliği sağlar. Siz burada sadece List sınıfını kullanarak String değerler tutan bir dizi oluşturdunuz. Eğer list sınıfı jenerik türde olmasaydı, List sınıfının aynısının birde int versiyonu olacaktı. Öğrenci numaralarını bir dizi içinde tutmak için bu sınıfı (int versiyonlu) kullanmak zorunda kalacaktınız. Liste sınıfını Object türde eleman içeren  tek bir sınıf yaparak onu kullanmak ise size tür dönüşümlerinde baya sıkıntı çıkaracaktı. List sınıfı jenerik türde olunca gereksiz kod önlendi ve esneklik sağlanmış oldu. 

Şimdi biraz örneklerle olayı anlamaya çalışalım.

Normalde biz bir listi şu şekilde tür bağımsız tanımlayabiliyorduk.

void main(){
  
  var list1 = List();
  
}

Bu tür bir liste dynamic yapılıdır. Ve add metoduyla istediğimiz object türünden değeri ekleyebiliriz.

void main(){

  var list1 = List();
  
  list1.add("Murat");
  list1.add(1);
  list1.add(false);
  
}

Bu kullanımda tüm liste elemanları dynamic türde olduğu için, liste elemanına eklediğiniz türün kendi özel fonksiyonlarını kullanamazsınız. Örneğin String türünün length adında, o değişkenin uzunluğunu size veren bir fonksiyonu vardır. Fakat dynamic türünde böyle bir fonksiyon size sunulmaz. Bu şekilde olunca, dynamic olan veriyi önce String türüne dönüştürüp, sonra onun altındaki özellikleri kullanırız. Bu zahmetli bir iştir. Gelecek olan türleriniz belli ise, her zaman değişken tanımlamalarınızda tür belirtmeniz sizin için faydalı olacaktır.

İşte tam bu noktada tür bağımlı generic listler devreye girer.

Tanımlama

List<T> ListName = List();

şeklinde olur.

Burada T yerine istediğimiz obj tipli değeri yazabiliriz. int, String, double ya da kendi yazdığımız sınıflar.

Bir kaç generic list tanımlayalım.

List<int> s1 = List();
  List<String> s2 = List();
  List<double> s3 = List();

Şimdi bu listelere bir kaç değer ekleyelim.

Şunu unutmamalıyız ki artık int tipli bir listeye int harici bir değer yazmaya çalışırsak hata alırız.

s1.add(1);
  s1.add(2);
  
  s2.add("Murat");
  s2.add("Meryem");
  
  s3.add(4.5);
  s3.add(5.6);

Generic listler üzerinde normal listler üzerinde direkt çalıştıramayacağımız pek çok özellike ve metod vardır.

Bunlara yeri geldikçe bakacağız.

Şimdi biz kendimiz bir sınıf yazalım ve bu sınıfın tipinden bir list üretelim ve değerler yazalım.

class Student{
  
  String name;
  String lastname;
  int no;
  String classroom;
  
}

Student sınıfımızı yazdık.

Şimdi bu sınıfa değerler atayıp listeye ekleyelim. Sonra bu listedeki değerleri print ile ekrana basalım.

void main(){

  var list1 = List();

  list1.add("Murat");
  list1.add(1);
  list1.add(false);

  List<int> s1 = List();
  List<String> s2 = List();
  List<double> s3 = List();

  s1.add(1);
  s1.add(2);

  s2.add("Murat");
  s2.add("Meryem");

  s3.add(4.5);
  s3.add(5.6);

  List<Student> student = List();
  Student Std = Student();
  Std.name = "Murat";
  Std.lastname = "Bilginer";
  Std.no = 20;
  Std.classroom = "9/A";

  student.add(Std);

  for(var i in student){
    print("${i.name} - ${i.lastname} - ${i.no} - ${i.classroom}");
  }
}

class Student{

  String name;
  String lastname;
  int no;
  String classroom;

}
List<Student> student = List();
  Student Std = Student();
  Std.name = "Murat";
  Std.lastname = "Bilginer";
  Std.no = 20;
  Std.classroom = "9/A";

  student.add(Std);

  for(var i in student){
    print("${i.name} - ${i.lastname} - ${i.no} - ${i.classroom}");
  }
H:\Dart_Tutorial\hello_dart\bin>dart 34_generics.dart
Murat - Bilginer - 20 - 9/A

Sınıfın yapıcı metoduna değerleri parametre olarak zorunlu kılarsak listeye şöylede ekleyebiliriz.

class Student{

  String name;
  String lastname;
  int no;
  String classroom;
  
  Student(this.name, this.lastname, this.no, this.classroom){
  }

}
 Student(this.name, this.lastname, this.no, this.classroom){
  }
student.add(new Student("Murat","Bilginer",30,"10/A"));
void main(){

  var list1 = List();

  list1.add("Murat");
  list1.add(1);
  list1.add(false);

  List<int> s1 = List();
  List<String> s2 = List();
  List<double> s3 = List();

  s1.add(1);
  s1.add(2);

  s2.add("Murat");
  s2.add("Meryem");

  s3.add(4.5);
  s3.add(5.6);

  List<Student> student = List();
  /* Student Std = Student();
  Std.name = "Murat";
  Std.lastname = "Bilginer";
  Std.no = 20;
  Std.classroom = "9/A";*/

  // student.add(Std);

  student.add(new Student("Murat","Bilginer",30,"10/A"));

  for(var i in student){
    print("${i.name} - ${i.lastname} - ${i.no} - ${i.classroom}");
  }
}

class Student{

  String name;
  String lastname;
  int no;
  String classroom;

  Student(this.name, this.lastname, this.no, this.classroom){
  }

}
H:\Dart_Tutorial\hello_dart\bin>dart 34_generics.dart
Murat - Bilginer - 30 - 10/A

Generic Listin elemanlarına index numaralarından da ulaşılabilir.

Liste uzunluğunun dışında bir index verirseniz hata alırsınız. Bunun için öncelikle length özelliği ile listenin uzunluğunu bulabilirsiniz.

Örneğin 5 elemanlı bir listenin length özelliği geriye 5 değerini dönecektir.

Ama biz listede 5. indexi yazdırmak istersek hata alırız. Çünkü listeler saymaya 0 dan başlıyordu. Dizinin son elemanı bu neden 4 nolu indextedir.

List<int> s4 = List();
  s4.add(2);
  s4.add(3);
  s4.add(5);
  s4.add(6);
  
  print(s4.length);
  print(s4[0]);
  print(s4[1]);
  print(s4[2]);
  print(s4[3]);
H:\Dart_Tutorial\hello_dart\bin>dart 34_generics.dart
Murat - Bilginer - 30 - 10/A
4
2
3
5
6

Genericlerin en kullanışlı olduğu alanlardan biri Sınıfların Generic değer alabilmesi ve bu değerin içeride işlenebiliyor olmasıdır.

Ne demek istiyorum?

Genericlerde T tüm objeleri temsil eder. Eğer sınıfa bir generic göndereceksek bunu T ile belirtebiliriz. Böylelikle çalışma anında T yerine ne gönderirsek işleme alacaktır.

Şimdi şöyle bir şey düşünelim.

Bir sınıfımız var ve kendisine obj tipli ne gönderirsek ekran printle değeri yazdırabilsin istiyoruz.

class Process<T> {
  final T obj;

  Process(this.obj);

  void printObj() {
    print(obj);
  }
}

Şimdi bu sınıftan nesne türetirken göndereceğimiz T object tipli olarak saklanır. Ve sınıfın metodu çağırıldığında gönderilen değeri ekrana basacakatır.

void main(){

  var intGeneric = Process(3);
  var doubleGeneric = Process(1.2);
  var stringGeneric = Process("Murat");
  var boolGeneric = Process(true);

  intGeneric.printObj(); 
  doubleGeneric.printObj();
  stringGeneric.printObj();
  boolGeneric.printObj();

}

class Process<T> {
  final T obj;

  Process(this.obj);

  void printObj() {
    print(obj);
  }
}

Sonuca bakalım.

H:\Dart_Tutorial\hello_dart\bin>dart 35_generic_classes.dart
3
1.2
Murat
true

Aklıma burada şöyle bir soru geliyor ya ben bu T yerine kendi oluşturduğum bir class’ı gönderseydim ekrana ne yazardı.

Yapalım.

void main(){

  var intGeneric = Process(3);
  var doubleGeneric = Process(1.2);
  var stringGeneric = Process("Murat");
  var boolGeneric = Process(true);


  intGeneric.printObj();
  doubleGeneric.printObj();
  stringGeneric.printObj();
  boolGeneric.printObj();

  Student std = Student("Murat", 243);
  var studentGeneric = Process(std);
  studentGeneric.printObj();


}

class Process<T> {
  final T obj;

  Process(this.obj);

  void printObj() {
    print(obj);
  }
}

class Student{
  String name;
  int no;

  Student(this.name, this.no){

  }
}
H:\Dart_Tutorial\hello_dart\bin>dart 35_generic_classes.dart
3
1.2
Murat
true
Instance of 'Student'

Sonuc olarak sadece Instance of Student değerini gördük.

Bu haliyle işime yaradığı söylenemez. Bu sınıflarımdan birden fazla sayıda olsaydı bu sefer T ifadesinin ortak değerlerine ulaşma imkanım olmayacaktı. Çünkü direkt olarak bağımsız T objlerinin metodlarına erişemezdi.

Böyle durumları ortadan kaldırmak için genelde generic sınıflar temel bir tür ile sınırlandırılır. Ve diğer sınıflar bu temel sınıftan türetilir. Böylece tüm sınıflar da aynı fonksiyon mevcut olacaktır.

İşte bunu yapabilmek için ortak iş yapan sınıflarımızı bir abstract sınıftan implement etmemiz gerekirdi.

Şimdi bunu yapalım.

abstract class Document {
  void printType();
}

class Pdf implements Document {
  @override
  void printType() {
    print("Pdf Document");
  }
}

class Word implements Document {
  @override
  void printType() {
    print("Word Document");
  }
}

class WriteGeneric<T extends Document> {
  final T obj;
  WriteGeneric(this.obj);

  void printDocumentType() {
    obj.printType();
  }
}

Şimdi bunları kullanalım.

Pdf pdf = Pdf();
  Word word = Word();
  var writePdfDocument = WriteGeneric<Pdf>(pdf);
  var writeWordDocument = WriteGeneric<Word>(word);

  writePdfDocument.printDocumentType();
  writeWordDocument.printDocumentType();
void main(){

  var intGeneric = Process(3);
  var doubleGeneric = Process(1.2);
  var stringGeneric = Process("Murat");
  var boolGeneric = Process(true);


  intGeneric.printObj();
  doubleGeneric.printObj();
  stringGeneric.printObj();
  boolGeneric.printObj();

  Student std = Student("Murat", 243);
  var studentGeneric = Process(std);
  studentGeneric.printObj();

  Pdf pdf = Pdf();
  Word word = Word();
  var writePdfDocument = WriteGeneric<Pdf>(pdf);
  var writeWordDocument = WriteGeneric<Word>(word);

  writePdfDocument.printDocumentType();
  writeWordDocument.printDocumentType();

}

class Process<T> {
  final T obj;

  Process(this.obj);

  void printObj() {
    print(obj);
  }
}

class Student{
  String name;
  int no;

  Student(this.name, this.no){

  }
}


abstract class Document {
  void printType();
}

class Pdf implements Document {
  @override
  void printType() {
    print("Pdf Document");
  }
}

class Word implements Document {
  @override
  void printType() {
    print("Word Document");
  }
}

class WriteGeneric<T extends Document> {
  final T obj;
  WriteGeneric(this.obj);

  void printDocumentType() {
    obj.printType();
  }
}
H:\Dart_Tutorial\hello_dart\bin>dart 35_generic_classes.dart
3
1.2
Murat
true
Instance of 'Student'
Pdf Document
Word Document

Generic konumuzda böyleydi.

Bir sonraki yazımda görüşmek üzere.


What's Your Reaction?

hate hate
0
hate
fail fail
0
fail
fun fun
0
fun
geeky geeky
0
geeky
love love
0
love
lol lol
0
lol
omg omg
0
omg
win win
0
win
Murat Bilginer
21 Şubat 1992 Doğumlu. Endüstri Mühendisi olarak Lisansını 2016 yılında tamamlamıştır. Industryolog Akademi - NGenius oluşumlarının kurucusudur. Şu anda kendi şirketi Brainy Tech ile hem Yazılım Hizmetleri Sunmakta Hem de Online Eğitimler Vermektedir.