Monday, January 12, 2015

[Java] - Abstract(soyut) sınıflar ve sınıflar arası kalıtım


Evet, başlıktan da anlayabileceğiniz üzere bugünkü konumuz object oriented programming'in en önemli konularından birisi olan soyut sınıflar ve kalıtım. İsterseniz önce bu kavramlara daha genel bakarak ne olduklarını anlayalım.

Kalıtım denince aklınıza ilk önce biyoloji gelir. Kalıtım, herhangi bir canlının kendi türündeki bir sonraki nesile aktardığı özelliklerin genelidir. Obje-tabanlı programlamada da mantık bu şekilde işler. Bunu basit bir örnekle açıklayalım.

İlk sınıfımız öğrenci sınıfı olsun;


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Student
{
 private int Age;
 private int Weight;
 private int Height;
 
 private int AverageGrade;
 private int Level;
 
 
 public int getAge(){return Age;}
 public int getWeight(){return Weight;}
 public int getHeight(){return Height;}
 public int getAvgGrade(){return AverageGrade;}
 public int getLevel(){return Level;}
 
 Student(int a, int w, int h) 
 {
  Age = a;
  Weight = w;
  Height = h;
 }

}
İkinci sınıfımız ise Asker sınıfı olsun;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Soldier 
{
 private int Age;
 private int Weight;
 private int Height;
 
 private int Rank;
 private int Badges;
 
 
 public int getAge(){return Age;}
 public int getWeight(){return Weight;}
 public int getHeight(){return Height;}
 public int getRank(){return Rank;}
 public int getBadges(){return Badges;}
 
 Soldier(int a, int w, int h) 
 {
  Age = a;
  Weight = w;
  Height = h;
 }
}

Farkettiğiniz üzere, bu iki sınıfın paylaştığı ortak değerler bulunmakta. Eğer biz bu iki sınıfı ortak bir çatı altında toplamak isteseydik, toplayabileceğimiz çatı muhtemelen 'kişi' olurdu. Ve bu kişi sınıfı, hem öğrenci hem askerdeki Yaş,Kilo ve Boy değişkenlerine sahip olurdu. Kalıtımın devreye girdiği nokta işte tam burası. Eğer ortak değişkenlere, fonksiyonlara sahip birden fazla sınıf yaratacaksak, kalıtım işimizi aşırı derecede kolaylaştırıyor. Şimdi kalıtımda inherit edilecek, yani özellikleri alt sınıflara aktarılacak sınıfı nasıl yaratacağımızı görelim.



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class Person 
{

 private int Age;
 private int Weight;
 private int Height;
 
 
 public int getAge(){return Age;}
 public int getWeight(){return Weight;}
 public int getHeight(){return Height;}
 
 Person(int a,int w, int h)
 {
  Age = a;
  Weight = w;
  Height = h;
 }
 
}

Üst sınıfımız, yani kalıtımın kaynağı olan sınıfa gördüğünüz gibi, alt sınıflarda paylaşılacak olan bütün değişkenleri ve fonksiyonları ekledik. Dikkat ettiyseniz Person sınıfı Soldier sınıfındaki Badges ve Rank değişkenlerini ve Student sınıfındaki AverageGrade ve Level değişkenlerini içermiyor. Bunun sebebi bu değişkenlerin tanımlandığı sınıfa özel değişken olmaları. Öğrenciler rütbeye ve madalyaya sahip olmazken, askerler de ortalama not ve sınıfa sahip değiller. Dolayısıyla bunlar genel çatıya dahil değil.

Şimdi yarattığımız Person sınıfının özelliklerini kalıtımla alt sınıflara nasıl aktaracağımızı görelim.

 Student sınıfı ;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Student extends Person
{
 private int AverageGrade;
 private int Level;
 
 public int getAvgGrade(){return AverageGrade;}
 public int getLevel(){return Level;}
 
 Student(int a, int w, int h) 
 {
  super(a,w,h);
 }
}

Soldier sınıfı;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Soldier extends Person
{
 private int Rank;
 private int Badges;
 
 public int getRank(){return Rank;}
 public int getBadges(){return Badges;}
 
 Soldier(int a, int w, int h) 
 {
  super(a, w, h);
 }
}

Öncelikle gözünüze çarpan ilk değişiklik muhtemelen her iki sınıftaki Age,Weight ve Height değişkenlerinin ve bunların getter methodlarının artık bulunmadığıdır. Ve sınıfımızın tanımlamasının yanına gelen 'extends' kelimesi de bizim için yeni bir olgu.

Herhangi bir sınıf adının yanına gelen extends kelimesi, herhangi bir özelliği kalıtımla üstten alacağı sınıfı belirtmek için kullanılır, yani 'public class Soldier extends Person' satırının Türkçe karşılığı, 'Asker sınıfı Person sınıfının bütün özelliklerini kalıtımla alır' olacaktır. Buradaki ilişkiyi baba-oğul ilişkisi gibi düşünebilirsiniz.

Aklınıza takılabilecek sorulardan bazılarını cevaplayayım. Mesela, 'Ben Soldier sınıfı için getAge() fonksiyonunu kullanabilir miyim?' gibi bir soru soracak olursanız, cevabı evet. Üst sınıftan inherit edilen bütün değişkenler ve bütün fonksiyonlar, alt sınıfta sanki o sınıfta tanımlanmış gibi kullanılabilir.

Gelelim constructor içerisindeki super(a,w,h); olayına. 'super' keywordü, üst sınıftaki herhangi bir fonksiyonu alt sınıf içerisinden çağırmak için kullanılır, Örnek vermek gerekirse, Soldier sınıfının constructoru içerisindeki super(a,w,h); Person sınıfının constructorunu çağırıyor. Constructor harici diğer fonksiyon ve değişkenlere erişmek içinse super. kullanılır.

Şimdi, işleri biraz daha karmaşıklaştıralım ve 'soyut' aleme geçelim :)

Abstract sınıflar, yani soyut sınıflar genellikle alt sınıflar için taslak teşkil ederler. Abstract sınıflar soyut tanımlamalar(fonksiyon vs.) içerebilirler, veya içermeyebilirler. Abstract sınıflarda soyut tanımlanan fonksiyonların gövdeleri yoktur ve inherit eden alt sınıflar bu fonksiyonları override(yeniden tanımlama) etmek zorundadırlar. Abstract sınıfların en önemli özelliği ise, kendi başlarına bir objeye atanamazlar. Yani AbstractClass ac = new AbstractClass(); tanımlaması yanlıştır. Abstract sınıflar sadece inherit edilebilirler.

Dip not : Eğer abstract sınıfı bir başka abstract sınıf inherit ediyorsa, inherit eden sınıf üst sınıftaki abstract methodları tanımlamak zorunda değildir.

Basit bir örnekle bu konuyu biraz daha aydınlatalım.

Eşkenar üçgen sınıfı;


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Eşkenar üçgen sınıfı
public class EquilateralTriangle 
{
 // Kenar uzunluğu
 private int a;
 
 EquilateralTriangle(int a)
 {
  this.a = a;
 }
 
 // Alan hesabı
 public double calculateArea()
 {
  return 3*a;
 }
 
 // Çevre hesabı
 public double calculatePerimeter()
 {
  // a kare çarpı kök üç bölü dört..
  return ((a*a) * Math.sqrt((double)3)) / 4;
 }
}

İkizkenar üçgen sınıfı;


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// İkizkenar üçgen
public class IsoscelesTriangle 
{
 private int a;
 private int height;
 private int floor;
 
 IsoscelesTriangle(int a,int height,int t)
 {
  this.a = a;
  this.height = a;
  this.floor = t;
 }
 
 
 // Alan hesabı
 public double calculateArea()
 {
  return (floor * height) / 2;
 }
 // Çevre hesabı
 public double calculatePerimeter()
 {
  return Math.pow(a, 2) + floor;
 }
}


Farkettiğiniz üzere bu iki sınıfta aynı adda iki fonksiyon var, fakat gövdeleri farklı. Çünkü, her üçgen bir alana ve çevreye sahiptir fakat farklı üçgenlerin alan ve çevre hesaplama formülleri farklıdır. Bu durumda soyut sınıflardan yararlanabiliriz. Yaratacağımız yeni soyut sınıf ise tabiki 'Triangle' sınıfı olacak.


(Abstract) Triangle sınıfımız;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public abstract class Triangle 
{
 // Her iki sınıfta da ortak, kenar uzunluğu
 protected int a;
 // Her iki sınıfta da ortak, yükseklik
 protected int h;
 
 Triangle(int a, int h)
 {
  this.a = a; 
  this.h = h;
 }
 public abstract double calculateArea();
 public abstract double calculatePerimeter();
}

Alt sınıfları tanımlamaya başlamadan önce burada birkaç şeye değinmek istiyorum. class kelimesinden önce kullandığımız 'abstract' keywordu tahmin edebileceğiniz gibi sınıfın soyut olduğunu belirtiyor. Abstract sınıfımızın içerisinde diğer sınıflarda olduğu gibi değişkenler bulunabilir.

En altta tanımlanan 2 soyut method ise, inherit edecek sınıflarda bulunan ortak fakat gövdeleri farklı olan methodlar. Şimdi yukarıdaki Eşkenar ve İkizkenar üçgen sınıflarını soyut sınıfı inherit ederek tekrardan tanımlayalım.


Eşkenar üçgen;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// Eşkenar üçgen sınıfı
public class EquilateralTriangle extends Triangle
{
 EquilateralTriangle(int a,int h)
 {
  super(a,h);
 }
 
 // Alan hesabı
 public double calculateArea()
 {
  return 3*a;
 }
 
 // Çevre hesabı
 public double calculatePerimeter()
 {
  // a kare çarpı kök üç bölü dört..
  return ((a*a) * Math.sqrt((double)3)) / 4;
 }
}
İkizkenar üçgen;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// İkizkenar üçgen
public class IsoscelesTriangle extends Triangle
{
 // Her iki sınıfta da ortak, kenar uzunluğu
 private int floor;
 IsoscelesTriangle(int a,int height,int t)
 {
  super(a,height);
  this.floor = t;
 }
 
 // Alan hesabı
 public double calculateArea()
 {
  return (floor * h) / 2;
 }
 // Çevre hesabı
 public double calculatePerimeter()
 {
  return Math.pow(a, 2) + floor;
 }
}

Yaptığımız tek şey daha önceki inheritance örneklerinde olduğu gibi extends keywordunu kullanarak Eşkenar ve İkizkenar üçgen sınıflarına Triangle sınıfını inherit etmek oldu.

Önemli noktalar;

 - Abstract tanımlı bir methodu, eğer inherit eden sınıf abstract değilse, sınıfta mutlaka implement etmeli, yani gövdeye kavuşturmalısınız. Aksi taktirde programınız derlenmeyecektir.

 - Üst sınıftaki değişkenlerin alt sınıflarda görünür olmasını istiyorsanız, protected veya public modifierlerini kullanmalısınız. 


Evet, bir makalenin daha sonuna gelmekle beraber, yarınki OOP finaline girecek olan arkadaşlarıma başarılar dilerim.

Sağlıcakla kalın :)
Mustafa K. Bilgisayar Müh.

Kahve kokusu, visual studio, uykusuzluk ve huzur.

1 comment: