Neden soyut alanlar değil?


102

Java sınıfları neden soyut yöntemlere sahip oldukları gibi soyut alanlara sahip olamıyor?

Örneğin: Aynı soyut temel sınıfı genişleten iki sınıfım var. Bu iki sınıfın her biri, içlerinde bir hata mesajı olan bir String sabiti dışında aynı olan bir yönteme sahiptir. Alanlar soyut olabilseydi, bu sabit soyutlamayı yapabilir ve yöntemi temel sınıfa çekebilirim. Bunun yerine, getErrMsg()bu durumda çağrılan , String'i döndüren, bu yöntemi iki türetilmiş sınıfta geçersiz kılan ve ardından yöntemi (şimdi soyut yöntemi çağıran) kaldırabilen bir soyut yöntem oluşturmalıyım.

Neden başlamak için alanı soyutlayamıyorum? Java buna izin verecek şekilde tasarlanmış olabilir mi?


3
Görünüşe göre, alanı sabit olmayan hale getirerek ve basitçe yapıcı aracılığıyla değeri sağlayarak, 2 sınıf yerine bir sınıfın 2 örneğiyle sonuçlanarak bu sorunu ortadan kaldırmış olabilirsiniz.
Nate

5
Bir süper sınıfta alanları soyutlayarak, her alt sınıfın bu alana sahip olması gerektiğini belirlediniz, bu nedenle bu soyut olmayan bir alandan farklı değildir.
Peter Lawrey

@peter, senin fikrini takip ettiğimden emin değilim. soyut sınıfta soyut olmayan bir sabit belirtilmişse, değeri tüm alt sınıflarda da sabittir. soyut olsaydı, değeri her alt sınıf tarafından uygulanmalı / sağlanmalıdır. yani, hiç aynı olmayacaktı.
liltitus27

1
@ liltitus27 Sanırım 3.5 yıl önceki amacım, soyut alanlara sahip olmanın, bir arayüzün kullanıcısını uygulamadan ayırma fikrini kırmak dışında pek değişmeyeceğiydi.
Peter Lawrey

Bu yardımcı olacaktır çünkü alt sınıfta özel alan açıklamalarına izin verebilir
geg

Yanıtlar:


105

Yapıcısında başlatılmış olan soyut sınıfınızda son bir alana sahip olarak tanımladığınız şeyi yapabilirsiniz (test edilmemiş kod):

abstract class Base {

    final String errMsg;

    Base(String msg) {
        errMsg = msg;
    }

    abstract String doSomething();
}

class Sub extends Base {

    Sub() {
        super("Sub message");
    }

    String doSomething() {

        return errMsg + " from something";
    }
}

Çocuk sınıfınız finali süper kurucu aracılığıyla başlatmayı "unutursa", derleyici, soyut bir yöntem uygulanmadığında olduğu gibi, bir hata uyarısı verecektir .


Burada deneyecek bir editörüm yok, ama bu benim de göndereceğim şeydi ..
Tim

6
"derleyici bir uyarı verecektir". Aslında, Çocuk yapıcısı var olmayan bir noargs yapıcısını kullanmaya çalışıyor olabilir ve bu bir derleme hatasıdır (bir uyarı değil).
Stephen C

Ve @Stephen C'nin yorumuna eklemek, "soyut bir yöntem uygulanmadığında olduğu gibi" de bir uyarı değil, bir hatadır.
Laurence Gonsalves

4
İyi cevap - bu benim için çalıştı. Yayınlandığından bu yana epey zaman geçtiğini biliyorum ama Baseilan edilmesi gerektiğini düşünüyorum abstract.
Steve Chambers

2
Yine de bu yaklaşımdan hoşlanmıyorum (gitmenin tek yolu bu olsa da) çünkü bir kurucuya fazladan bir argüman ekler. Ağ programlamasında, her yeni mesajın soyut bir tip uyguladığı ancak AcceptMessage için 'A' ve LoginMessage için 'L' gibi benzersiz bir tanımlanmış karaktere sahip olması gereken mesaj türleri yapmayı seviyorum. Bunlar, özel bir mesaj protokolünün bu ayırt edici karakteri kabloya koyabilmesini sağlar. Bunlar sınıf için örtüktür, bu yüzden en iyi şekilde kurucuya aktarılmış bir şey olarak değil, alanlar olarak sunulacaklardır.
Adam Hughes

8

Bunun bir anlamı yok. Fonksiyonu soyut sınıfa taşıyabilir ve sadece bazı korumalı alanları geçersiz kılabilirsiniz. Bunun sabitlerle çalışıp çalışmadığını bilmiyorum ama etkisi aynı:

public abstract class Abstract {
    protected String errorMsg = "";

    public String getErrMsg() {
        return this.errorMsg;
    }
}

public class Foo extends Abstract {
    public Foo() {
       this.errorMsg = "Foo";
    }

}

public class Bar extends Abstract {
    public Bar() {
       this.errorMsg = "Bar";
    }
}

Demek istediğin errorMsg, alt sınıflarda uygulamayı / geçersiz kılma / her ne olursa olsun zorlamak istiyorsun ? Yöntemin temel sınıfta olmasını istediğini ve o zamanlar alanla nasıl başa çıkacağını bilmediğini sanıyordum.


5
Tabii bunu yapabilirim. Ve bunu düşünmüştüm. Ama türetilmiş her sınıfta bu değeri geçersiz kılacağımı bildiğim halde neden temel sınıfta bir değer belirlemem gerekiyor? Argümanınız, soyut yöntemlere izin vermenin bir değeri olmadığını savunmak için de kullanılabilir.
Paul Reiners

1
Eh bu şekilde değil, her alt sınıf vardır değerini geçersiz kılmak. Ayrıca protected String errorMsg;, alt sınıflarda değeri ayarladığımı bir şekilde zorlayanları da tanımlayabilirim . Tüm yöntemleri yer tutucu olarak uygulayan soyut sınıflara benzer, böylece geliştiricilerin ihtiyaç duymasalar da her yöntemi uygulaması gerekmez.
Felix Kling

7
Söyleyen protected String errorMsg;yok değil sen alt sınıflarda değeri ayarlayın zorunlu kılmak.
Laurence Gonsalves

1
Ayarlamazsanız, değerin boş olması "yaptırım" olarak sayılırsa, yaptırım dışı bırakma olarak ne adlandırırsınız?
Laurence Gonsalves

9
@felix, yaptırım ve sözleşmeler arasında bir fark var . bu yazının tamamı, herhangi bir alt sınıf tarafından yerine getirilmesi gereken bir sözleşmeyi temsil eden soyut sınıflar etrafında toplanmıştır. Bu nedenle, soyut bir alana sahip olmaktan bahsettiğimizde, herhangi bir alt sınıfın sözleşme başına bu alanın değerini uygulaması gerektiğini söylüyoruz . Yaklaşımınız herhangi bir değeri garanti etmez ve bir sözleşmenin yaptığı gibi bekleneni açıklığa kavuşturmaz. çok, çok farklı.
liltitus27

3

Açıkçası olabilir buna izin şekilde tasarlanmıştır, ancak yorganın altında hala dinamik sevk ve dolayısıyla bir yöntem çağrısı yapmak gerekir. Java'nın tasarımı (en azından ilk günlerde) bir dereceye kadar minimalist olma girişimiydi. Yani, tasarımcılar, halihazırda dilde bulunan diğer özelliklerle kolayca simüle edilebiliyorsa, yeni özellikler eklemekten kaçınmaya çalıştılar.


@Laurence - soyut alanların dahil edilmiş olabileceği bana açık değil . Bunun gibi bir şeyin yazı sistemi ve dil kullanılabilirliği üzerinde derin ve temel bir etkisi olması muhtemeldir. (Çoklu kalıtımın, dikkatsiz C ++ programcısını ısırtan ... derin problemler getirmesi gibi.)
Stephen C

0

Başlığınızı okurken, soyut örnek üyelerine atıfta bulunduğunuzu düşündüm; ve onlar için fazla bir kullanım göremedim. Ancak soyut statik üyeler tamamen başka bir konudur.

Java'da aşağıdaki gibi bir yöntem bildirebilmeyi sık sık diledim:

public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

Temel olarak, üst sınıfımın somut uygulamalarının belirli bir imzaya sahip statik bir fabrika yöntemi sağladığında ısrar etmek isterim. Bu, somut bir sınıfa atıfta bulunmamı Class.forName()ve seçtiğim bir kongre içinde bir tane oluşturabileceğimden emin olmamı sağlardı.


1
Evet, peki ... bu muhtemelen yansımayı çok fazla kullandığınız anlamına geliyor !!
Stephen C

Bana garip bir programlama alışkanlığı gibi geliyor. Class.forName (..) bir tasarım hatası gibi kokuyor.
viskisierra

Bu günlerde temelde bu tür şeyleri başarmak için her zaman Spring DI kullanıyoruz; ancak bu çerçeve bilinmeden ve popüler hale gelmeden önce, özelliklerde veya XML dosyalarında uygulama sınıflarını belirtiyorduk, sonra bunları yüklemek için Class.forName () kullanıyorduk.
Drew Wills

Size onlar için bir kullanım durumu verebilirim. "Soyut alanlar" neredeyse imkansız bulunmaması Genel soyut bir sınıfta bir jenerik tipte bir alan ilan etmek: @autowired T fieldName. Eğer Tböyle bir şey olarak tanımlanır T extends AbstractController, tip silme alan türü olarak hazırlanmayacağından neden olur AbstractControllerve onu değil somut ve benzersiz değil çünkü hangi autowired edilemez. Genelde onların pek bir faydası olmadığına katılıyorum ve bu yüzden dile ait değiller. Kabul etmeyeceğim şey, onların hiçbir faydası olmadığıdır.
DaBlick

0

Diğer bir seçenek, alanı temel sınıfta genel (isterseniz son) olarak tanımlamak ve ardından o anda hangi alt sınıfın kullanıldığına bağlı olarak bu alanı temel sınıfın yapıcısında başlatmaktır. Döngüsel bir bağımlılık getirmesi nedeniyle biraz gölgeli. Ama en azından bu değişebilen bir bağımlılık değil - yani, alt sınıf ya var olacak ya da olmayacak, ancak alt sınıfın yöntemleri ya da alanları değerini etkileyemez field.

public abstract class Base {
  public final int field;
  public Base() {
    if (this instanceof SubClassOne) {
      field = 1;
    } else if (this instanceof SubClassTwo) {
      field = 2;
    } else {
      // assertion, thrown exception, set to -1, whatever you want to do 
      // to trigger an error
      field = -1;
    }
  }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.