Java'da Kalıtım, Bölüm 1: extends anahtar sözcüğü

Java, kalıtım ve kompozisyon yoluyla sınıfın yeniden kullanımını destekler. Bu iki bölümlük eğitim, Java programlarınızda kalıtımı nasıl kullanacağınızı öğretir. Bölüm 1'de extends, bir üst sınıftan bir alt sınıf türetmek için anahtar kelimeyi nasıl kullanacağınızı, ana sınıf yapıcılarını ve yöntemleri çağırmayı ve yöntemleri geçersiz kılmayı öğreneceksiniz . Bölüm 2'de java.lang.Object, diğer her sınıfın miras aldığı Java'nın süper sınıfı olan bir tura çıkacaksınız .

Kalıtım hakkındaki öğrenmenizi tamamlamak için, kalıtıma karşı kompozisyonu ne zaman kullanacağınızı açıklayan Java ipucuma göz atmayı unutmayın. Kompozisyonun neden kalıtım için önemli bir tamamlayıcı olduğunu ve Java programlarınızda kapsülleme ile ilgili sorunlara karşı nasıl korunacağını öğreneceksiniz.

indir Kodu alın Bu eğitimdeki örnek uygulamalar için kaynak kodunu indirin. JavaWorld için Jeff Friesen tarafından düzenlendi.

Java kalıtımı: İki örnek

Kalıtım , yazılım geliştiricilerin kategoriler arasında bir ilişki kurmak için kullandıkları bir programlama yapısıdır . Kalıtım, daha genel olanlardan daha spesifik kategoriler türetmemizi sağlar. Daha spesifik kategori , bir tür daha genel kategoridir. Örneğin, çek hesabı, para yatırabileceğiniz ve çekebileceğiniz bir tür hesaptır. Benzer şekilde kamyon, büyük eşyaları taşımak için kullanılan bir tür araçtır.

Kalıtım, çok sayıda seviyeden geçerek giderek daha spesifik kategorilere yol açabilir. Örnek olarak, Şekil 1, araçtan miras alan araba ve kamyonu göstermektedir; arabadan devralan istasyon vagonu; ve kamyondan devralan çöp kamyonu. Oklar, daha spesifik "alt" kategorilerden (aşağıdan aşağıya) daha az spesifik "ana" kategorilere (yukarıdan yukarıya) doğru işaret eder.

Jeff Friesen

Bu örnek, bir alt kategorinin durum ve davranışları hemen bir üst kategoriden devraldığı tekli kalıtımı göstermektedir . Bunun tersine, çoklu miras , bir alt kategorinin iki veya daha fazla doğrudan üst kategoriden durum ve davranışları devralmasını sağlar. Şekil 2'deki hiyerarşi, çoklu kalıtımı göstermektedir.

Jeff Friesen

Kategoriler sınıflara göre tanımlanmıştır. Java , bir sınıfın, bu sınıfı genişleterek başka bir sınıftan doğrudan erişilebilir alanları ve yöntemleri miras aldığı sınıf uzantısı aracılığıyla tek mirası destekler . Ancak Java, sınıf uzantısı aracılığıyla çoklu mirası desteklemez.

Bir kalıtım hiyerarşisini görüntülerken, bir baklava deseninin varlığıyla çoklu kalıtımı kolayca tespit edebilirsiniz. Şekil 2, bu modeli araç, kara taşıtı, su aracı ve hoverkraft bağlamında göstermektedir.

Extends anahtar kelimesi

Java, extendsanahtar kelime aracılığıyla sınıf uzantısını destekler . Mevcut olduğunda, extendsiki sınıf arasında bir ebeveyn-çocuk ilişkisini belirtir. Kullandığım Aşağıda extendssınıflar arasındaki bir ilişki kurmak Vehicleve Carve ardından arasındaki Accountve SavingsAccount:

Liste 1. extendsAnahtar kelime bir ebeveyn-çocuk ilişkisini belirtir

class Vehicle { // member declarations } class Car extends Vehicle { // inherit accessible members from Vehicle // provide own member declarations } class Account { // member declarations } class SavingsAccount extends Account { // inherit accessible members from Account // provide own member declarations }

extendsAnahtar kelime sınıf adından sonra ve başka bir sınıf adından önce belirtilir. Önündeki sınıf adı extendsçocuğu tanımlar ve sınıf adı extendsebeveyni tanımlar. extendsJava, sınıf tabanlı çoklu kalıtımı desteklemediğinden, birden çok sınıf adı belirtmek imkansızdır .

: Bu örnekler kodlanması ilişkiler-bir olan Carbir edilir uzmanlaşmış Vehicleve SavingsAccountbir olduğunu uzmanlaşmış Account. Vehicleve temel sınıflar , üst sınıflar veya üst sınıflarAccount olarak bilinir . ve türetilmiş sınıflar , alt sınıflar veya alt sınıflar olarak bilinir .CarSavingsAccount

Final sınıfları

Uzatılmaması gereken bir sınıf ilan edebilirsiniz; örneğin güvenlik nedenleriyle. Java'da, finalbazı sınıfların genişletilmesini önlemek için anahtar kelimeyi kullanırız. Bir sınıf başlığının önüne final, olduğu gibi final class Password. Bu bildirim göz önüne alındığında, derleyici, birisi genişletme girişiminde bulunursa bir hata bildirecektir Password.

Alt sınıflar, erişilebilir alanları ve yöntemleri üst sınıflarından ve diğer atalarından devralır. Ancak asla kurucuları miras almazlar. Bunun yerine, alt sınıflar kendi oluşturucularını bildirir. Ayrıca, ebeveynlerinden ayırmak için kendi alanlarını ve yöntemlerini beyan edebilirler. Liste 2'yi düşünün.

Liste 2. Bir Accountebeveyn sınıfı

class Account { private String name; private long amount; Account(String name, long amount) { this.name = name; setAmount(amount); } void deposit(long amount) { this.amount += amount; } String getName() { return name; } long getAmount() { return amount; } void setAmount(long amount) { this.amount = amount; } }

Liste 2, her ikisi de yapıcıda ayarlanmış olan bir ada ve bir başlangıç ​​tutarına sahip genel bir banka hesabı sınıfını açıklamaktadır. Ayrıca, kullanıcıların para yatırmalarını sağlar. (Negatif miktarlarda para yatırarak para çekme işlemi yapabilirsiniz, ancak bu olasılığı göz ardı edeceğiz.) Hesap oluşturulurken hesap adının ayarlanması gerektiğini unutmayın.

Para birimi değerlerini temsil etmek

kuruş sayısı. Parasal değerleri saklamak için a doubleveya a kullanmayı tercih floatedebilirsiniz, ancak bunu yapmak yanlışlıklara yol açabilir. Daha iyi bir çözüm için, BigDecimalJava'nın standart sınıf kitaplığının bir parçası olan düşünün .

Liste 3 SavingsAccount, Accountana sınıfını genişleten bir çocuk sınıfı sunar .

Liste 3. Bir SavingsAccountalt sınıf, Accountüst sınıfını genişletir

class SavingsAccount extends Account { SavingsAccount(long amount) { super("savings", amount); } }

SavingsAccountEk alanları veya yöntemleri beyan etmek gerekmez çünkü sınıf önemsiz olduğunu. Ancak, Accountüst sınıfındaki alanları başlatan bir kurucu bildirir . Başlatma, Account's yapıcısı Java'nın superanahtar sözcüğü aracılığıyla çağrıldığında ve ardından parantezli bir argüman listesi geldiğinde gerçekleşir .

Süper () ne zaman ve nerede aranır

Tıpkı this()aynı sınıfta başka oluşturucu çağıran bir yapıcı ilk unsur olmalıdır super(), üst sınıfında bir kurucu çağıran bir yapıcı ilk unsur olmalıdır. Bu kuralı ihlal ederseniz, derleyici bir hata bildirir. Derleyici, super()bir yöntemde bir çağrı algılarsa da bir hata bildirir ; sadece super()bir kurucu çağırın .

Liste 4 Account, bir CheckingAccountsınıfla daha da genişler .

Liste 4. Bir CheckingAccountalt sınıf, Accountüst sınıfını genişletir

class CheckingAccount extends Account { CheckingAccount(long amount) { super("checking", amount); } void withdraw(long amount) { setAmount(getAmount() - amount); } }

CheckingAccountSavingsAccountbir withdraw()yöntem bildirmesinden biraz daha önemlidir . Bu yöntemin çağrıları dikkat setAmount()ve getAmount(), CheckingAccountdevralır Account. İçindeki amountalana doğrudan erişemezsiniz Accountçünkü bu alan bildirilmiştir private(bkz. Liste 2).

super () ve argümansız yapıcı

Eğer super()bir alt sınıf yapıcısı belirtilmedi ve üst sınıf bir beyan etmezse no-argumentyapıcı, daha sonra derleyici bir hata bildirir. Bunun nedeni, alt no-argumentsınıf yapıcısının super(), mevcut olmadığında bir üst sınıf kurucusunu çağırması gerektiğidir .

Sınıf hiyerarşisi örneği

I've created an AccountDemo application class that lets you try out the Account class hierarchy. First take a look at AccountDemo's source code.

Listing 5. AccountDemo demonstrates the account class hierarchy

class AccountDemo { public static void main(String[] args) { SavingsAccount sa = new SavingsAccount(10000); System.out.println("account name: " + sa.getName()); System.out.println("initial amount: " + sa.getAmount()); sa.deposit(5000); System.out.println("new amount after deposit: " + sa.getAmount()); CheckingAccount ca = new CheckingAccount(20000); System.out.println("account name: " + ca.getName()); System.out.println("initial amount: " + ca.getAmount()); ca.deposit(6000); System.out.println("new amount after deposit: " + ca.getAmount()); ca.withdraw(3000); System.out.println("new amount after withdrawal: " + ca.getAmount()); } }

The main() method in Listing 5 first demonstrates SavingsAccount, then CheckingAccount. Assuming Account.java, SavingsAccount.java, CheckingAccount.java, and AccountDemo.java source files are in the same directory, execute either of the following commands to compile all of these source files:

javac AccountDemo.java javac *.java

Execute the following command to run the application:

java AccountDemo

You should observe the following output:

account name: savings initial amount: 10000 new amount after deposit: 15000 account name: checking initial amount: 20000 new amount after deposit: 26000 new amount after withdrawal: 23000

Method overriding (and method overloading)

A subclass can override (replace) an inherited method so that the subclass's version of the method is called instead. An overriding method must specify the same name, parameter list, and return type as the method being overridden. To demonstrate, I've declared a print() method in the Vehicle class below.

Listing 6. Declaring a print() method to be overridden

class Vehicle { private String make; private String model; private int year; Vehicle(String make, String model, int year) { this.make = make; this.model = model; this.year = year; } String getMake() { return make; } String getModel() { return model; } int getYear() { return year; } void print() { System.out.println("Make: " + make + ", Model: " + model + ", Year: " + year); } }

Next, I override print() in the Truck class.

Listing 7. Overriding print() in a Truck subclass

class Truck extends Vehicle { private double tonnage; Truck(String make, String model, int year, double tonnage) { super(make, model, year); this.tonnage = tonnage; } double getTonnage() { return tonnage; } void print() { super.print(); System.out.println("Tonnage: " + tonnage); } }

Truck's print() method has the same name, return type, and parameter list as Vehicle's print() method. Note, too, that Truck's print() method first calls Vehicle's print() method by prefixing super. to the method name. It's often a good idea to execute the superclass logic first and then execute the subclass logic.

Calling superclass methods from subclass methods

In order to call a superclass method from the overriding subclass method, prefix the method's name with the reserved word super and the member access operator. Otherwise you will end up recursively calling the subclass's overriding method. In some cases a subclass will mask non-private superclass fields by declaring same-named fields. You can use super and the member access operator to access the non-private superclass fields.

To complete this example, I've excerpted a VehicleDemo class's main() method:

Truck truck = new Truck("Ford", "F150", 2008, 0.5); System.out.println("Make = " + truck.getMake()); System.out.println("Model = " + truck.getModel()); System.out.println("Year = " + truck.getYear()); System.out.println("Tonnage = " + truck.getTonnage()); truck.print();

The final line, truck.print();, calls truck's print() method. This method first calls Vehicle's print() to output the truck's make, model, and year; then it outputs the truck's tonnage. This portion of the output is shown below:

Make: Ford, Model: F150, Year: 2008 Tonnage: 0.5

Use final to block method overriding

Occasionally you might need to declare a method that should not be overridden, for security or another reason. You can use the final keyword for this purpose. To prevent overriding, simply prefix a method header with final, as in final String getMake(). The compiler will then report an error if anyone attempts to override this method in a subclass.

Method overloading vs overriding

Suppose you replaced the print() method in Listing 7 with the one below:

void print(String owner) { System.out.print("Owner: " + owner); super.print(); }

The modified Truck class now has two print() methods: the preceding explicitly-declared method and the method inherited from Vehicle. The void print(String owner) method doesn't override Vehicle's print() method. Instead, it overloads it.

Bir alt sınıfın yöntem başlığının önüne @Overrideek açıklama ekleyerek, bir yöntemi derleme zamanında geçersiz kılmak yerine aşırı yükleme girişimini algılayabilirsiniz :

@Override void print(String owner) { System.out.print("Owner: " + owner); super.print(); }

Belirtmek @Override, derleyiciye verilen yöntemin başka bir yöntemi geçersiz kıldığını söyler. Bunun yerine birisi yöntemi aşırı yüklemeye çalıştıysa, derleyici bir hata bildirir. Bu açıklama olmadan, yöntem aşırı yüklemesi yasal olduğundan derleyici bir hata bildirmez.

@Override ne zaman kullanılır?

İle geçersiz kılma yöntemlerini önekleme alışkanlığını geliştirin @Override. Bu alışkanlık, aşırı yükleme hatalarını çok daha erken tespit etmenize yardımcı olacaktır.