Ana içeriğe geç

Java'da OOP: Sınıflar, Nesneler, Kapsülleme, Kalıtım ve Soyutlama

Pratik örneklerle Java’da Nesne Yönelimli Programlamayı öğrenin. Bir restoran menü sistemi kullanarak sınıflar, nesneler, kalıtım, kapsülleme ve soyut sınıflarda uzmanlaşın.
· 15 dk. oku

Yapay Zeka ile Keşfedin

Java, dünya genelinde en popüler üç dil arasında istikrarlı şekilde yer alır. Kurumsal yazılım geliştirme, Android mobil uygulamaları ve büyük ölçekli web uygulamaları gibi alanlardaki benimsenmesi benzersizdir. Güçlü tip sistemi, geniş ekosistemi ve “bir kez yaz, her yerde çalıştır” özelliği, sağlam ve ölçeklenebilir sistemler kurmak için onu özellikle cazip kılar. Bu yazıda, Java’nın nesne yönelimli programlama özelliklerinin geliştiricilerin bu yeteneklerden etkin şekilde yararlanmasına nasıl olanak tanıdığını, doğru kod organizasyonu ve yeniden kullanım yoluyla bakımı kolay ve ölçeklenebilir uygulamalar geliştirmelerini nasıl sağladığını inceleyeceğiz.

Java Kodunu Düzenleme ve Çalıştırma Üzerine Bir Not

Herhangi bir kod yazmaya başlamadan önce biraz kurulum yapalım.

Sözdiziminde olduğu gibi, Java’nın kod organizasyonu konusunda da katı kuralları vardır.

Öncelikle, her public sınıf kendi dosyasında olmalı ve dosya adı sınıf adıyla birebir aynı olup .java uzantısına sahip olmalıdır. Örneğin, bir Laptop sınıfı yazmak istersem, dosya adı mutlaka Laptop.java—büyük/küçük harf duyarlı olmalıdır. Aynı dosyada public olmayan sınıflar bulunabilir ancak bunları ayırmak en iyisidir. Farkındayım, daha sınıfları yazmadan önce nasıl organize edileceğinden bahsederek önden gidiyoruz; ancak önceden neleri nereye koyacağımıza dair kabaca bir fikir edinmek iyi bir fikirdir.

Tüm Java projelerinde Main.java dosyası ve içinde Main sınıfı bulunur. Sınıflarınızı buradan oluşturup nesneler yaratarak test edersiniz.

Java kodunu çalıştırmak için popüler bir Java IDE’si olan IntelliJ IDEA’yı kullanacağız. IntelliJ’i kurduktan sonra:

  1. Yeni bir Java projesi oluşturun (File > New > Project)
  2. src klasörüne sağ tıklayarak Main.java dosyasını oluşturun ve aşağıdaki içeriği yapıştırın:
public class Main {
   public static void main(String[] args) {
       // Create and tests objects here
      
   }
}

Sınıflardan bahsederken, kodu Main.java dışındaki dosyalara yazarız. Ancak nesneler oluşturma ve test etmeden bahsediyorsak Main.java’ya geçeriz.

Programı çalıştırmak için main metodunun yanındaki yeşil oynat düğmesine tıklayabilirsiniz:

IntelliJ IDEA'da Java için bir editör penceresinin ekran görüntüsü

Çıktı altta, Run araç penceresinde gösterilecektir.

Java’ya tamamen yeniyseniz, lütfen devam etmeden önce Java veri tipleri ve kontrol akışının temellerini ele alan Introduction to Java kursumuza göz atın.

Aksi halde, doğrudan konuya girelim.

Java Sınıfları ve Nesneleri

Peki, sınıflar tam olarak nedir?

Sınıflar, gerçek dünyadaki kavramları temsil etmek için Java’daki programlama yapılarıdır. Örneğin, şu MenuItem sınıfını düşünün (IDE’nizde bu sınıfı yazmak için bir dosya oluşturun):

public class MenuItem {
   public String name;
   public double price;
}

Sınıf, bir restorandaki çeşitli menü öğelerini temsil etmek için bize bir plan ya da şablon sunar. Sınıfın iki özelliğini, name, ve price, değiştirerek burger veya salata gibi sayısız menü nesnesi oluşturabiliriz.

Java’da bir sınıf oluşturmak için, sınıfın erişim seviyesini (private, public veya protected) belirten bir satır yazar, ardından sınıf adını verirsiniz. Süslü parantezlerden hemen sonra, sınıfınızın özniteliklerini tanımlarsınız.

Peki, bu sınıfa ait nesneleri nasıl oluştururuz? Java bunu kurucu (constructor) metodlarla sağlar:

public class MenuItem {
   public String name;
   public double price;
  
   // Constructor
   public MenuItem(String name, double price) {
       this.name = name;
       this.price = price;
   }
}

Kurucu, bir sınıftan yeni bir nesne oluşturduğumuzda çağrılan özel bir metottur. Nesnenin özniteliklerini sağladığımız değerlerle başlatır. Yukarıdaki örnekte kurucu, name ve price parametrelerini alır ve ‘this’ anahtar kelimesini kullanarak bunları gelecekteki nesne örneğine atar.

Kurucunun sözdizimi diğer sınıf metotlarından farklıdır çünkü dönüş tipi belirtmenizi gerektirmez. Ayrıca, kurucu sınıfla aynı ada sahip olmalı ve sınıf tanımından sonra tanımladığınız özniteliklerle uyumlu olmalıdır. Yukarıda, kurucu iki öznitelik oluşturuyor çünkü sınıf tanımından sonra iki öznitelik ilan ettik: name ve price.

Sınıfınızı ve kurucusunu yazdıktan sonra, main metodunuzda bu sınıfın örneklerini (nesnelerini) oluşturabilirsiniz:

public class Main {
   public static void main(String[] args) {
       // Create objects here
       MenuItem burger = new MenuItem("Burger", 3.5);
       MenuItem salad = new MenuItem("Salad", 2.5);
       System.out.println(burger.name + ", " + burger.price);
   }
}

Çıktı:

Burger, 3.5

Yukarıda, iki MenuItem nesnesini burger ve salad değişkenlerine atıyoruz. Java’da zorunlu olduğu üzere, değişkenin tipi ilan edilmelidir; burada bu tip MenuItem’dır. Ardından, sınıfımızın bir örneğini oluşturmak için new anahtar kelimesini yazar ve kurucu metodu çağırırız.

Kurucuya ek olarak, sınıfınıza davranış kazandıran normal metotlar da oluşturabilirsiniz. Örneğin, aşağıda vergi sonrası toplam fiyatı hesaplayan bir metot ekliyoruz:

public class MenuItem {
   public String name;
   public double price;
  
   // Constructor
   public MenuItem(String name, double price) {
       this.name = name;
       this.price = price;
   }
  
   // Method to calculate price after tax
   public double getPriceAfterTax() {
       double taxRate = 0.08; // 8% tax rate
       return price + (price * taxRate);
   }
}

Artık vergi dahil toplam fiyatı hesaplayabiliriz:

public class Main {
   public static void main(String[] args) {
       MenuItem burger = new MenuItem("Burger", 3.5);
       System.out.println("Price after tax: $" + burger.getPriceAfterTax());
   }
}

Çıktı:

Price after tax: $3.78

Kapsülleme

Sınıfların amacı, nesneler oluşturmak için bir şablon sağlamaktır. Bu nesneler daha sonra diğer betikler veya programlar tarafından kullanılacaktır. Örneğin, MenuItem nesnelerimiz, adlarını, fiyatlarını ve görsellerini ekranda gösteren bir kullanıcı arayüzü tarafından kullanılabilir.

Bu nedenle, sınıflarımızı, örneklerinin yalnızca bizim amaçladığımız şekilde kullanılabileceği biçimde tasarlamalıyız. Şu anda MenuItem sınıfımız çok temel ve hataya açık. Biri saçma özniteliklerle nesneler oluşturabilir; örneğin negatif fiyatlı elmalı turta veya milyon dolarlık sandviç:

// Inside Main.java
MenuItem applePie = new MenuItem("Apple Pie", -5.99);  // Negative price!
MenuItem sandwich = new MenuItem("Sandwich", 1000000); // Unreasonably expensive

System.out.println("Apple pie price: $" + applePie.price);
System.out.println("Sandwich price: $" + sandwich.price);

Dolayısıyla, bir sınıf yazdıktan sonra yapılacak ilk iş, özniteliklerinin nasıl oluşturulup erişildiğini sınırlayarak onları korumaktır. Başlangıç olarak, price için yalnızca pozitif değerler kabul etmek ve yanlışlıkla aşırı pahalı öğelerin görüntülenmesini önlemek için bir üst sınır belirlemek istiyoruz.

Java bunu setter metotlarıyla yapmamıza izin verir:

public class MenuItem {
   private String name;
   private double price;
   private static final double MAX_PRICE = 100.0;
  
   public MenuItem(String name, double price) {
       this.name = name;
       setPrice(price);
   }
  
   public void setPrice(double price) {
       if (price < 0) {
           throw new IllegalArgumentException("Price cannot be negative");
       }
       if (price > MAX_PRICE) {
           throw new IllegalArgumentException("Price cannot exceed $" + MAX_PRICE);
       }
       this.price = price;
   }
  
}

Yukarıdaki kod bloğunda nelerin yeni olduğuna bakalım:

1. private anahtar kelimesini ekleyerek öznitelikleri private yaptık. Bu, onlara yalnızca MenuItem sınıfı içinde erişilebileceği anlamına gelir. Kapsülleme bu kritik adımla başlar.

2. Şu yeni sabiti ekledik: MAX_PRICE. Bu sabit şunları taşır:

  • private (yalnızca sınıf içinde erişilebilir)
  • static (tüm örnekler arasında ortak)
  • final (başlatıldıktan sonra değiştirilemez)
  • makul bir üst sınır olarak 100,0 $’a ayarlanmıştır

3. setPrice() metodunu ekledik; bu metot:

  • Bir price parametresi alır
  • Fiyatın negatif olmadığını doğrular
  • Fiyatın MAX_PRICE’ı aşmadığını doğrular
  • Doğrulama başarısız olursa açıklayıcı mesajlarla IllegalArgumentException fırlatır
  • Tüm doğrulamalar geçerse fiyatı ayarlar

4. Kurucuyu, fiyatı doğrudan atamak yerine setPrice() kullanacak şekilde değiştirdik. Bu, nesne oluşturma sırasında fiyat doğrulamasının yapılmasını sağlar.

Az önce iyi nesne yönelimli tasarımın temel sütunlarından birini uyguladık — kapsülleme. Bu paradigma, veri gizliliğini ve nesne özniteliklerine kontrollü erişimi dayatır; böylece dahili uygulama ayrıntıları dış müdahaleden korunur ve yalnızca iyi tanımlanmış arayüzler aracılığıyla değiştirilebilir.

Kapsüllemeyi name özniteliğine uygulayarak konuyu pekiştirelim. Sadece latte, cappuccino, espresso, americano ve mocha servis eden bir kahve dükkânımız olduğunu hayal edin.

Dolayısıyla menü öğesi adlarımız yalnızca bu listedeki öğelerden biri olabilir. Bunu koda şöyle dökebiliriz:

// Rest of the class here
...
private static final String[] VALID_NAMES = {"latte", "cappuccino", "espresso", "americano", "mocha"};
private String name;
public void setName(String name) {
   String lowercaseName = name.toLowerCase();
   for (String validName : VALID_NAMES) {
       if (validName.equals(lowercaseName)) {
           this.name = name;
           return;
       }
   }
   throw new IllegalArgumentException("Invalid drink name. Must be one of: " + String.join(", ", VALID_NAMES));
}

Yukarıdaki kod, bir kahve dükkânındaki menü öğeleri için ad doğrulamasını uygular. Bunu parçalayalım:

1. Önce, yalnızca izin verilen içecek adlarını içeren private static final bir dizi olan VALID_NAMES tanımlanır: latte, cappuccino, espresso, americano ve mocha. Bu dizinin özellikleri şunlardır:

  • private: yalnızca sınıf içinde erişilebilir
  • static: tüm örnekler için ortaktır
  • final: başlatıldıktan sonra değiştirilemez

2. İçecek adını depolamak için private bir String name alanı ilan edilir

3. setName() metodu doğrulama mantığını uygular:

  • String türünde bir name parametresi alır
  • Karşılaştırmayı büyük-küçük harf duyarsız yapmak için küçük harfe çevirir
  • VALID_NAMES dizisi üzerinde döngü kurar
  • Eşleşme bulunursa adı ayarlar ve döner
  • Eşleşme bulunamazsa, tüm geçerli seçenekleri listeleyen açıklayıcı bir mesajla IllegalArgumentException fırlatır

İşte şu ana kadarki tam sınıf:

public class MenuItem {
   private String name;
   private double price;

   private static final String[] VALID_NAMES = {"latte", "cappuccino", "espresso", "americano", "mocha"};

   private static final double MAX_PRICE = 100.0;

   public MenuItem(String name, double price) {
       setName(name);
       setPrice(price);
   }

   public void setName(String name) {
       String lowercaseName = name.toLowerCase();
       for (String validName : VALID_NAMES) {
           if (validName.equals(lowercaseName)) {
               this.name = name;
               return;
           }
       }
       throw new IllegalArgumentException("Invalid drink name. Must be one of: " + String.join(", ", VALID_NAMES));
   }

   public void setPrice(double price) {
       if (price < 0) {
           throw new IllegalArgumentException("Price cannot be negative");
       }
       if (price > MAX_PRICE) {
           throw new IllegalArgumentException("Price cannot exceed $" + MAX_PRICE);
       }
       this.price = price;
   }
}

Özniteliklerin nasıl oluşturulduğunu koruduktan sonra, onlara nasıl erişildiğini de korumak isteriz. Bu, getter metotları kullanılarak yapılır:

public class MenuItem {
   // Rest of the code here
   ...
   public String getName() {
       return name;
   }
   public double getPrice() {
       return price;
   }
}

Getter metotları, bir sınıfın private özniteliklerine kontrollü erişim sağlar. Doğrudan öznitelik erişiminin yol açabileceği istenmeyen değişiklikleri önleyerek kapsüllemenin bozulmasını engellerler.

Örneğin, getter olmadan özniteliklere doğrudan erişebiliriz:

MenuItem item = new MenuItem("Latte", 4.99);
String name = item.name; // Direct access to attribute
item.name = "INVALID"; // Can modify directly, bypassing validation

Getter’larla doğru erişimi zorunlu kılarız:

MenuItem item = new MenuItem("Latte", 4.99);
String name = item.getName(); // Controlled access through getter
// item.name = "INVALID"; // Not allowed - must use setName() which validates

Bu kapsülleme şunları sağlar:

  1. Geçersiz değişiklikleri önleyerek veri bütünlüğünü korur
  2. Sınıfı kullanan kodu etkilemeden dahili uygulamayı değiştirmemize imkân tanır
  3. Gerektiğinde ek mantık içerebilecek tek bir erişim noktası sunar
  4. Kodu daha bakımı kolay ve hatalara daha az yatkın hale getirir

Kalıtım

Sınıfımız iyi görünmeye başladı ama oldukça fazla sorun var. Örneğin, birçok türde yemek ve içecek servis eden büyük bir restoran için bu sınıf yeterince esnek değil.

Farklı türde yiyecek öğeleri eklemek istersek çeşitli zorluklarla karşılaşırız. Bazı yemekler paket servise uygundur, bazıları anında tüketilmelidir. Menü öğelerinin farklı fiyat ve indirimleri olabilir. Yemeklerin sıcaklık takibi veya özel depolama gerektirebilir. İçecekler sıcak ya da soğuk olabilir ve özelleştirilebilir malzemeler içerebilir. Öğeler alerjen bilgisi ve porsiyon seçenekleri gerektirebilir. Mevcut sistem bu farklı gereksinimleri karşılamıyor.

Kalıtım bu sorunların tümüne zarif bir çözüm sunar. Ortak özniteliklere sahip temel bir MenuItem sınıfı tanımlamamıza ve ardından bu temelleri miras alırken kendine özgü özellikler ekleyen alt sınıflar oluşturmamıza olanak tanır. 

Örneğin, sıcaklık seçenekleri olan içecekler için bir Drink sınıfı, anında tüketilmesi gereken öğeler için bir Food sınıfı ve özel depolama gerektiren öğeler için bir Dessert sınıfımız olabilir — hepsi çekirdek menü öğesi işlevselliğini miras alır.

Sınıfları genişletme

Bu fikirleri Drink ile başlayarak hayata geçirelim:

public class Drink extends MenuItem {
   private boolean isCold;
  
   public Drink(String name, double price, boolean isCold) {
       this.name = name;
       this.price = price;
       this.isCold = isCold;
   }
  
   public boolean getIsCold() {
       return isCold;
   }
  
   public void setIsCold(boolean isCold) {
       this.isCold = isCold;
   }
}

Bir ebeveynden miras alan bir alt sınıf tanımlamak için, alt sınıf adından sonra extends anahtar kelimesini ve ardından ebeveyn sınıfı yazarız. Sınıf tanımından sonra, bu alt sınıfın sahip olduğu yeni öznitelikleri tanımlar ve kurucusunu uygularız.

Ancak dikkat edin, name ve price başlatmalarını isCold ile birlikte tekrarlamak zorunda kalıyoruz. Bu ideal değildir çünkü ebeveyn sınıfın yüzlerce özniteliği olabilir. Ayrıca, yukarıdaki kod derleme sırasında hata verecektir çünkü ebeveyn sınıfın özniteliklerini başlatmanın doğru yolu bu değildir. Doğru yol super anahtar kelimesini kullanmaktır:

public class Drink extends MenuItem {
   private boolean isCold;
  
   public Drink(String name, double price, boolean isCold) {
       super(name, price);
       this.isCold = isCold;
   }
  
   public boolean getIsCold() {
       return isCold;
   }
  
   public void setIsCold(boolean isCold) {
       this.isCold = isCold;
   }
}

super anahtar kelimesi, ebeveyn sınıfın kurucusunu çağırmak için kullanılır. Bu durumda super(name, price), bu öznitelikleri başlatmak için MenuItem’ın kurucusunu çağırır ve kod tekrarını önler. Yalnızca Drink sınıfına özgü yeni isCold özniteliğini başlatmamız gerekir.

Bu anahtar kelime oldukça esnektir çünkü alt sınıfın herhangi bir kısmında ebeveyn sınıfa atıfta bulunmak için kullanılabilir. Örneğin, bir ebeveyn metodunu çağırmak için super.methodName(), bir öznitelik için ise super.attributeName kullanılır.

Metot geçersiz kılma (overriding)

Şimdi, sınıflarımıza vergi sonrası toplam fiyatı hesaplayan yeni bir metot eklemek istediğimizi düşünelim. Farklı menü öğelerinin farklı vergi oranları olabileceğinden (örneğin, hazırlanmış yiyecekler ve paketli içecekler), ebeveyn sınıfta ortak bir metot adı korurken her alt sınıfta belirli vergi hesaplamalarını uygulamak için metot geçersiz kılmayı kullanabiliriz.

Bu şöyle görünür:

public class MenuItem {
   // Rest of the MenuItem class
  
   public double calculateTotalPrice() {
       // Default tax rate of 10%
       return price * 1.10;
   }
}
public class Food extends MenuItem {
   private boolean isVegetarian;
  
   public Food(String name, double price, boolean isVegetarian) {
       super(name, price);
       this.isVegetarian = isVegetarian;
   }
  
   @Override
   public double calculateTotalPrice() {
       // Food has 15% tax
       return super.getPrice() * 1.15;
   }
}
public class Drink extends MenuItem {
   private boolean isCold;
  
   public Drink(String name, double price, boolean isCold) {
       super(name, price);
       this.isCold = isCold;
   }
  
   @Override
   public double calculateTotalPrice() {
       // Drinks have 8% tax
       return super.getPrice() * 1.08;
   }
}

Bu örnekte metot geçersiz kılma, her alt sınıfın calculateTotalPrice() metoduna kendi uygulamasını sağlamasına olanak tanır:

Temel MenuItem sınıfı varsayılan olarak %10 vergi hesabı tanımlar.

Food ve Drink sınıfları MenuItem’ı genişlettiğinde, bu metodu kendi vergi oranlarını uygulamak üzere geçersiz kılar:

  • Yiyecek öğelerinde %15 gibi daha yüksek vergi oranı
  • İçeceklerde %8 gibi daha düşük vergi oranı

@Override açıklama satırı, bu metotların ebeveyn sınıf metodunu geçersiz kıldığını açıkça belirtmek için kullanılır. Bu, metot imzası ebeveyn sınıfla eşleşmezse hataları yakalamaya yardımcı olur.

Her alt sınıf yine de ebeveyn sınıfın fiyatına super.getPrice() ile erişebilir; bu da geçersiz kılınan metotların, kendi davranışlarını eklerken ebeveyn sınıf işlevselliğinden nasıl yararlanabileceğini gösterir.

Kısacası, metot geçersiz kılma, alt sınıfların ebeveyn sınıfta tanımlanan metotlara kendi uygulamalarını sağlamasına olanak tanıyan ve aynı metot imzasını koruyan kalıtımın ayrılmaz bir parçasıdır.

Soyut Sınıflar

MenuItem sınıf hiyerarşimiz çalışıyor, ancak bir sorun var: herkes düz bir MenuItem nesnesi oluşturabilmeli mi? Sonuçta restoranımızda her menü öğesi ya Food ya da Drink’tir — “genel bir menü öğesi” diye bir şey yok.

Bunu, MenuItem’ı soyut bir sınıf yaparak önleyebiliriz. Soyut bir sınıf yalnızca bir temel şablon sunar — yalnızca kalıtım için ebeveyn sınıf olarak kullanılabilir, doğrudan örneklenemez.

MenuItem’ı soyut yapmak için erişim belirleyicisinden sonra abstract anahtar kelimesini ekleriz:

public abstract class MenuItem {
   private String name;
   private double price;
  
   public MenuItem(String name, double price) {
       setName(name);
       setPrice(price);
   }
  
   // Existing getters/setters remain the same
  
   // Make this method abstract - every subclass MUST implement it
   public abstract double calculateTotalPrice();
}

Soyut sınıflar, yukarıdaki calculateTotalPrice() gibi soyut metotlar da içerebilir. Bu soyut metotlar, alt sınıfları kendi uygulamalarını sağlamaya zorlayan bir sözleşme işlevi görür. Başka bir deyişle, soyut bir sınıftaki her soyut metot, alt sınıflar tarafından uygulanmalıdır.

Öyleyse, bu değişiklikleri dikkate alarak Food ve Drink sınıflarını yeniden yazalım:

public class Food extends MenuItem {
   private boolean isVegetarian;
  
   public Food(String name, double price, boolean isVegetarian) {
       super(name, price);
       this.isVegetarian = isVegetarian;
   }
  
   @Override
   public double calculateTotalPrice() {
       return getPrice() * 1.15;  // 15% tax
   }
}
public class Drink extends MenuItem {
   private boolean hasCaffeine;
  
   public Drink(String name, double price, boolean hasCaffeine) {
       super(name, price);
       this.hasCaffeine = hasCaffeine;
   }
  
   @Override
   public double calculateTotalPrice() {
       return getPrice() * 1.10;  // 10% tax
   }
}

Bu menü sistemi uygulamasıyla, soyutlama ve kalıtımın farklı iş gereksinimlerine kolayca uyum sağlayabilen esnek ve bakımı kolay kod oluşturmak için nasıl birlikte çalıştığını gördük.

Sonuç

Bugün, Java’nın bir nesne yönelimli programlama dili olarak neler yapabildiğine kısa bir bakış attık. Bir restoran menü sistemi üzerinden sınıfların, nesnelerin ve OOP’nin birkaç temel sütununun: kapsülleme, kalıtım ve soyutlamanın temellerini ele aldık.

Bu sistemi üretime hazır hale getirmek için hâlâ arayüzler (soyutlamanın bir parçası), çok biçimlilik (polymorphism) ve OOP tasarım kalıpları gibi öğrenecek pek çok şey var. Bu kavramlar hakkında daha fazla bilgi edinmek için Introduction to OOP in Java kursumuza bakın.

Java bilginizi test etmek isterseniz, Java Mülakat Soruları makalemizde yer alan bazı soruları yanıtlamayı deneyin.

Java'da OOP SSS

Bu Java OOP eğitimini takip etmek için önkoşullar nelerdir?

Veri tipleri, değişkenler, kontrol akışı (if/else, döngüler) ve temel sözdizimi dahil olmak üzere Java programlamaya dair temel bilgilere sahip olmalısınız. Bir hatırlatmaya ihtiyaç duyarsanız, Introduction to Java kursumuz bu temelleri kapsar.

OOP kavramlarını açıklamak için neden bir restoran menü sistemi kullanılıyor?

Bir restoran menü sistemi, OOP ilkelerinin gerçek dünyadaki uygulamalarını gösteren sezgisel bir örnektir. Doğal olarak kalıtımı (farklı menü öğesi türleri), kapsüllemeyi (fiyat ve ad değerlerinin korunması) ve arayüzleri (sadakat programları, sıcaklık kontrolü) sergiler. Çoğu insan restoranların nasıl çalıştığını bildiğinden, kavramlar daha kolay anlaşılır.

Java'da soyut sınıflar ile arayüzler arasındaki fark nedir?
  • Bir sınıf yalnızca bir soyut sınıfı genişletebilir ancak birden çok arayüzü uygulayabilir.
  • Soyut sınıflar alanlara ve kuruculara sahip olabilir; arayüzler olamaz.
  • Soyut sınıflar hem soyut hem somut metotlara sahip olabilir; arayüzler (Java 8’den önce) geleneksel olarak yalnızca soyut metotlara sahiptir.
  • Soyut sınıflar bir temel uygulama sağlar, arayüzler ise bir sözleşme tanımlar.
Bexruz (Bex) Tuychiev

Yazar

Bexruz (Bex) Tuychiev

2 yılı aşkın deneyime sahip bir veri bilimi içerik üreticisiyim ve Medium’da en büyük takipçi kitlelerinden birine sahibim. Yapay zekâ ve makine öğrenimi üzerine, biraz da alaycı bir üslupla yazılmış, ayrıntılı makaleler kaleme almayı seviyorum; çünkü onları biraz daha az sıkıcı hale getirmek için bir şeyler yapmak gerekiyor. 130’un üzerinde makale ve bir de DataCamp kursu ürettim; bir diğeri de hazırlık aşamasında. İçeriklerim 5 milyondan fazla kişi tarafından görüldü; bunların 20 bini Medium ve LinkedIn’de takipçim oldu. 

Konular

İlgili