Neden super.super.method (); Java'da izin verilmiyor mu?


360

Bu soruyu okudum ve eğer yazabilirse kolayca çözüleceğini düşündüm (onsuz çözülemez değil):

@Override
public String toString() {
    return super.super.toString();
}

Birçok durumda yararlı olup olmadığından emin değilim, ama neden olmadığını ve diğer dillerde böyle bir şey olup olmadığını merak ediyorum .

Siz ne düşünüyorsunuz?

EDIT: Açıklığa kavuşturmak için: evet biliyorum, bu Java'da imkansız ve gerçekten kaçırmıyorum. Bu çalışmayı beklediğim bir şey değil ve bir derleyici hatası almak şaşırdı. Sadece bir fikrim vardı ve tartışmayı seviyorum.


7
Çağırmak istemek, super.super.toString()bir sınıfı genişletmeyi seçtiğinizde kendi kararınızla çelişir, böylece tüm özelliklerini (bazılarını değil) kabul eder.
DayaMoon

Yanıtlar:


485

Kapsüllemeyi ihlal ediyor. Üst sınıfın davranışını atlayamamanız gerekir. Bazen kendi sınıfınızın davranışını (özellikle aynı yöntemden) atlayabilmeniz mantıklıdır, ancak ebeveyninizin davranışını geçemez. Örneğin, bir temel "öğeler koleksiyonumuz", "kırmızı öğeler koleksiyonunu" temsil eden bir alt sınıfımız ve "büyük kırmızı öğeler koleksiyonunu" temsil eden bir alt sınıfımız olduğunu varsayalım. Sahip olmak mantıklı:

public class Items
{
    public void add(Item item) { ... }
}

public class RedItems extends Items
{
    @Override
    public void add(Item item)
    {
        if (!item.isRed())
        {
            throw new NotRedItemException();
        }
        super.add(item);
    }
}

public class BigRedItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        if (!item.isBig())
        {
            throw new NotBigItemException();
        }
        super.add(item);
    }
}

Bu iyi - RedItems her zaman içerdiği öğelerin tamamen kırmızı olduğundan emin olabilir. Şimdi varsayalım idi super.super.add () aramak mümkün:

public class NaughtyItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        // I don't care if it's red or not. Take that, RedItems!
        super.super.add(item);
    }
}

Şimdi istediğimizi ekleyebiliriz ve içindeki değişmez RedItemsbozulur.

bu mantıklı mı?


38
güzel bir örnek. ama temel sınıf öğeleri kabul ettiğinde kötü bir tasarım olduğunu düşündüm, ancak türetilmiş sınıf onları reddediyor, çünkü türetilmiş temel sınıf için bir yedek değiştirme olarak kullanılamıyor (ikame ihlali ilkesi). doğru düşünce mi, yoksa böyle bir hiyerarşi temiz mi?
Johannes Schaub - litb

5
Yani miras üzerine kompozisyon mu demek istiyorsun? Bu örnek kalıtımdan kaçınmak için bir neden değildir - başka birçok şey olmasına rağmen.
Jon Skeet

3
@Tim: Bu, kapsüllemeyi ihlal etmenin anlaşılması kolay bir örneğiydi. Bunun yerine bir özellik ayarlıyor olabilir. Ayrıca, tüm yönler bir tür düzeyinde görünmeyecektir. Jenerik her şeyin cevabı değildir.
Jon Skeet

12
@ JohannesSchaub-litb Liskov ikame ilkesini ihlal ettiğini düşünüyorum, çünkü eğer biri Item sözleşmesini kodlar ve RedItems örneğini kullanırsa, beklenmedik NotRedItemException elde edilir. Her zaman bir alt sınıfın süper bir girdi kümesi alması ve bir çıktı alt kümesi döndürmesi gerektiği öğretildi. Başka bir deyişle, bir alt sınıf hiçbir zaman süper sınıf için geçerli olan bir girdiyi reddetmemeli veya süper sınıfın üretemediği çıktı üretmemelidir; bu, süper sınıfın atmadığı bir hata atmayı içerir.
Konstantin Tarashchanskiy

4
@piechuckerr: Bazen kitaplar, bazen blog yazıları, bazen sadece deneyim ...
Jon Skeet

72

Bence Jon Skeet doğru cevaba sahip. Bunu eklemek gibi olur yapabilirsiniz döküm yoluyla üst sınıfı süper sınıf gölgede kalan değişkenleri erişmek this:

interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
        int x = 3;
        void test() {
                System.out.println("x=\t\t"          + x);
                System.out.println("super.x=\t\t"    + super.x);
                System.out.println("((T2)this).x=\t" + ((T2)this).x);
                System.out.println("((T1)this).x=\t" + ((T1)this).x);
                System.out.println("((I)this).x=\t"  + ((I)this).x);
        }
}

class Test {
        public static void main(String[] args) {
                new T3().test();
        }
}

çıktı üreten:

x = 3
super.x = 2
((T2) bunu) .x = 2
((T1) bunu) .x = 1
((I) bunu) .x = 0

( JLS'den örnek )

Ancak, yöntem çağrıları nesnenin çalışma zamanı türüne göre belirlendiği için bu yöntem yöntem çağrıları için çalışmaz.


6
Üst sınıftaki adla aynı ada sahip bir değişkeniniz varsa ve bir nedenden ötürü değiştiremezsiniz (veya değiştirmenize izin verilmez), üst sınıf değişkenine aynı adla erişmeye devam edebilirsiniz. Bunun ne yararı var? ...Ey ... Hiç kullanmadým.
Michael Myers

İfadeler belgesine mükemmel bağlantı. OCPJP için okuyan insanlar için bazı güzel örnekler.
Gordon

Muhteşem. Hiç bir kadro kullanmayı düşünmezdim.
Thomas Eding

nasıl geliyor sınıf T1 geçersiz kılma değişkeni x? statik final varsayılan olarak değil mi?
amarnath harish

@ amarnathharish: Geçersiz kılınmaz ve alanlar varsayılan olarak nihai olarak paket korumalıdır.
Michael Myers

41

Aşağıdaki kod çoğu durumda super.super ... super.method () kullanmaya izin düşünüyorum. (bunu yapmak çirkin olsa bile)

Kısacası

  1. ata türünün geçici örneğini oluşturma
  2. alanların değerlerini orijinal nesneden geçici nesneye kopyala
  3. geçici nesnede hedef yöntemi çağır
  4. değiştirilen değerleri orijinal nesneye geri kopyala

Kullanımı:

public class A {
   public void doThat() { ... }
}

public class B extends A {
   public void doThat() { /* don't call super.doThat() */ }
}

public class C extends B {
   public void doThat() {
      Magic.exec(A.class, this, "doThat");
   }
}


public class Magic {
    public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance,
            String methodOfParentToExec) {
        try {
            Type type = oneSuperType.newInstance();
            shareVars(oneSuperType, instance, type);
            oneSuperType.getMethod(methodOfParentToExec).invoke(type);
            shareVars(oneSuperType, type, instance);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz,
            SourceType source, TargetType target) throws IllegalArgumentException, IllegalAccessException {
        Class<?> loop = clazz;
        do {
            for (Field f : loop.getDeclaredFields()) {
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                f.set(target, f.get(source));
            }
            loop = loop.getSuperclass();
        } while (loop != Object.class);
    }
}

10
Yansıması ile her şeyi yapabilirsiniz, evet :) Hatta dizeleri değişebilir yapabilirsiniz.
BalusC

23
Ne korkunç bir şey! Sadece nasıl yapılacağını bulmak için sana +1 veriyorum :)
Larry Watanabe

6
Bu güzel bir hile ama bu bile her zaman havasız, henüz gerekli) super.super'i çağırmakla eşdeğer değildir ve bunun nedeni super.super aramasının C (C + B + A) bağlamını taşıyacağı ve cevaplarınızın bir örnek oluşturduğu Örneğin, getContext () adı verilen her doThat ve getContext her sınıfta farklı uygulanmışsa, bu cevap çalışmaz. cevabınızda A'nın getContext () yöntemini kullanırken, kullanılamayan super.super öğesinin çağrılması C'nin getContext öğesinin kullanılmasına neden olur.
inor

Hmm. Bazı durumlarda, belki de inor'un dinamik proxy'lere ( javahowto.blogspot.co.uk/2011/12/… ) itirazının üstesinden gelebilir , yöntem yönlendirme orijinal nesneye yönlendirilir (her çağrıdan sonra değişkenleri senkronize eder)? Proxy'lerin her şeyin bir arabirim uygulamasını gerektirdiği görülüyor. Ayrıca, süper süper sınıfın özellikle süper süper yöntemlerinden birini çağırmasının mümkün olup olmadığını merak ediyorum ve bunları yönlendirmemeniz gerekmiyor ...
Erhannis

Başka bir yorumda, engellemenin super.super.programcıları bir geçici çözüm arayışında kendilerini ayağa vurmanın yeni, kıvrımlı ve berbat yollarını bulmaya davet ettiğini söyledim , bu bunun mükemmel bir örneği, çünkü meslektaşlarınız muhtemelen bir şeyler yazmak için çok nefret edecek kişisel ve tam anlamıyla sizi ayağından vuracakları gibi. +1
Braden Best

11

Yorum yapmak için yeterli itibarım yok, bu yüzden bunu diğer cevaplara ekleyeceğim.

Jon Skeet güzel bir örnekle mükemmel cevap veriyor. Matt B'nin bir anlamı vardır: tüm üst sınıfların süperleri yoktur. Süper olmayan bir süper süper çağırdıysanız kodunuz kırılacaktı.

Nesneye yönelik programlama (Java'nın bulunduğu) işlevlerle değil nesnelerle ilgilidir. Göreve yönelik programlama istiyorsanız, C ++ veya başka bir şey seçin. Nesneniz süper sınıfa uymuyorsa, onu "büyükbaba veya büyükanne sınıfına" eklemeniz, yeni bir sınıf oluşturmanız veya sığabileceği başka bir süper bulmanız gerekir.

Şahsen, bu sınırlamanın Java'nın en güçlü yanlarından biri olduğunu gördüm. Kod kullandığım diğer dillere göre biraz katı, ama her zaman ne bekleyeceğimi biliyorum. Bu, Java'nın "basit ve tanıdık" hedefine yardımcı olur. Aklımda, super.super demek basit veya tanıdık değil. Belki de geliştiriciler aynı şeyi hissettiler?


3
"Bütün üst sınıfların süperleri yok" diyorsunuz. Eh, java.lang.Object dışında size "null" ne verebilir. Yani diyebilirim ki, neredeyse hepsinin süperleri var.
Tim Büthe

Uygulama programcısının yazdığı her sınıfta bir "süper" vardır (java.lang.Object yazmaz, ancak uygulama programcısı bunu yazmaz.)
finnw

2
Super.super.super ... süper bir derleme zamanı hatası yaparak çok fazla süper varsa kolayca çözülür. Java'nın yalnızca genel mirası olduğu düşünüldüğünde, birisi miras hiyerarşisini değiştirirse, arabirimi değiştirirsiniz. Bu yüzden super ^ n'nin korkutucu olmasından endişe etmem.
Thomas Eding

7

Bunu yapmak için iyi nedenler var. Yanlış uygulanmış bir yöntemi olan bir alt sınıfınız olabilir, ancak üst yöntem doğru bir şekilde uygulanır. Üçüncü taraf bir kitaplığa ait olduğu için, kaynağı değiştiremeyebilirsiniz / değiştiremeyebilirsiniz. Bu durumda, bir alt sınıf oluşturmak, ancak super.super yöntemini çağırmak için bir yöntemi geçersiz kılmak istersiniz.

Diğer bazı posterlerde gösterildiği gibi, bunu yansıma yoluyla yapmak mümkündür, ancak böyle bir şey yapmak mümkün olmalıdır

(SuperSuperClass this) .theMethod ();

Şu anda bu sorunla ilgileniyorum - hızlı düzeltme süper sınıf yöntemini sububclass yöntemine kopyalayıp yapıştırmaktır :)


1
Bir yöntemi çağırmadan önce yayınlamak, yetkilendirilen yöntemi değiştirmez — Her zaman kullanılan alt sınıfın uygulamasıdır, asla süper değildir. Bakınız, örneğin, stackoverflow.com/questions/1677993/…
Joshua Goldberg

1
@Larry tam olarak içinde bulunduğum durum ve kullandığım düzeltme. İyi karar!
bcr

Gerekli yöntem kodunuz varsa, gerekli tüm alanları açabilir ve bu kodu alt sınıfınızda çalıştırabilirsiniz.
Enyby

6

Başkalarının belirttiği çok iyi noktalara ek olarak, başka bir neden olduğunu düşünüyorum: üst sınıf bir üst sınıf yoksa?

Her sınıf doğal olarak uzandığından (en azından) Object, super.whatever()her zaman üst sınıftaki bir yönteme atıfta bulunacaktır. Ama ya sınıfınız sadece uzanıyorsa Object- super.supero zaman ne ifade ederdi ? Bu davranış nasıl ele alınmalıdır - derleyici hatası, NullPointer, vb?

Sanırım buna izin verilmemesinin başlıca nedeni kapsüllemeyi ihlal etmesidir, ancak bu da küçük bir neden olabilir.


4
Açıkçası, bu bir derleyici hatası olacaktır - ve derleyicinin sahip olduğu bilgilerdir.
Michael Borgwardt

4

Bir yöntemin üzerine yazar ve tüm süper sınıf sürümüne (örneğin, diyelim equals) istiyorsanız, hemen hemen her zaman önce doğrudan üst sınıf sürümünü çağırmak istersiniz, hangisi isterse üst sınıf sürümünü çağırır. .

Ben sadece bir yöntemin bazı keyfi süper sınıf sürümünü çağırmak için nadiren (hiç değilse. Bir durumda düşünemiyorum) mantıklı olduğunu düşünüyorum. Java ile bunun mümkün olup olmadığını bilmiyorum. C ++ ile yapılabilir:

this->ReallyTheBase::foo();

6
Birisi yanlış uygulanan bir yöntemle bir alt sınıf yazmışsa, ancak üst sınıf yöntemi işin% 90'ını yaparsa mantıklıdır. Daha sonra bir alt sınıf yapmak ve üst sınıf süper sınıf yöntemini çağıran yöntemi geçersiz kılmak ve kendi% 10'unuzu eklemek istersiniz.
Larry Watanabe

3

Bir tahminde, çünkü o kadar sık ​​kullanılmıyor. Bunu kullanarak görebilmemin tek nedeni, doğrudan ebeveyninizin bazı işlevleri geçersiz kılmış olması ve orijinaline geri yüklemeye çalışmanızdır.

Bu bana göre OO ilkelerine aykırı gibi görünüyor çünkü sınıfın doğrudan ebeveyni sınıfınızla büyükanne veya büyükbabadan daha yakından ilişkili olmalıdır.


3

Bak bu Github proje, özellikle objectHandle değişken. Bu proje, bir torun üzerinde büyükbaba veya büyükanne yönteminin nasıl ve doğru bir şekilde çağrıldığını gösterir.

Bağlantının kopması durumunda, kod İşte:

import lombok.val;
import org.junit.Assert;
import org.junit.Test;

import java.lang.invoke.*;

/*
Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
Please don't actually do this... :P
*/
public class ImplLookupTest {
    private MethodHandles.Lookup getImplLookup() throws NoSuchFieldException, IllegalAccessException {
        val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
        field.setAccessible(true);
        return (MethodHandles.Lookup) field.get(null);
    }

    @Test
    public void test() throws Throwable {
        val lookup = getImplLookup();
        val baseHandle = lookup.findSpecial(Base.class, "toString",
            MethodType.methodType(String.class),
            Sub.class);
        val objectHandle = lookup.findSpecial(Object.class, "toString",
            MethodType.methodType(String.class),
            // Must use Base.class here for this reference to call Object's toString
            Base.class);
        val sub = new Sub();
        Assert.assertEquals("Sub", sub.toString());
        Assert.assertEquals("Base", baseHandle.invoke(sub));
        Assert.assertEquals(toString(sub), objectHandle.invoke(sub));
    }

    private static String toString(Object o) {
        return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode());
    }

    public class Sub extends Base {
        @Override
        public String toString() {
            return "Sub";
        }
    }

    public class Base {
        @Override
        public String toString() {
            return "Base";
        }
    }
}

Mutlu Kodlama !!!!


Bilim adamlarınız yapabilecekleri veya yapamayacakları konusunda çok meşgullerdi, yapmaları gerekip gerekmediğini düşünmek için durmadılar. Lütfen bunu gerçekten yapmayın ...: P 🤔
Tim Büthe

Evet, bunlar kodlayıcının sözleri, benim değil. Bence, aslında bir gün ihtiyacınız olabileceğini düşünüyorum
kyay

2

Mümkünse super.super yöntem gövdesini başka bir yönteme koyarım

class SuperSuperClass {
    public String toString() {
        return DescribeMe();
    }

    protected String DescribeMe() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    public String toString() {
        return DescribeMe();
    }
}

Veya super-super sınıfını değiştiremiyorsanız, bunu deneyebilirsiniz:

class SuperSuperClass {
    public String toString() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return DescribeMe(super.toString());
    }

    protected String DescribeMe(string fromSuper) {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    protected String DescribeMe(string fromSuper) {
        return fromSuper;
    }
}

Her iki durumda da,

new ChildClass().toString();

I super super "için arama sonuçları


Kendimi SuperSuperClass ve ChildClass'a sahip olduğum bir durumda buldum ama SuperClass değil, bu yüzden ilk çözümü faydalı buldum.
xofon


1
public class A {

     @Override
     public String toString() {
          return "A";
     }

}


public class B extends A {

     @Override
     public String toString() {
          return "B";
     }

}

public class C extends B {

     @Override
     public String toString() {
          return "C";
     }

}


public class D extends C {

     @Override
     public String toString() {
          String result = "";
          try {
                result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString();
          } catch (InstantiationException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          } catch (IllegalAccessException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          }
          return result;
     }

}

public class Main {

     public static void main(String... args) {
          D d = new D();
          System.out.println(d);

     }
}

run: BİR YAPI BAŞARILI (toplam süre: 0 saniye)


1
Anlıyorum, ancak nesnenin herhangi bir durumu varsa bu işe yaramayacak yeni bir örnek oluşturuyorsunuz.
Tim Büthe

1

Temel sınıf kodunu değiştiremediğinizde super.super.method () yönteminin çağrılması mantıklıdır. Bu genellikle varolan bir kitaplığı genişlettiğinizde olur.

Önce kendinize sorun, neden bu sınıfı genişletiyorsunuz? Cevabınız "değiştiremediğimden" ise, uygulamanızda tam paket ve sınıf oluşturabilir ve yaramaz yöntemi yeniden yazabilir veya temsilci oluşturabilirsiniz:

package com.company.application;

public class OneYouWantExtend extends OneThatContainsDesiredMethod {

    // one way is to rewrite method() to call super.method() only or 
    // to doStuff() and then call super.method()

    public void method() {
        if (isDoStuff()) {
            // do stuff
        }
        super.method();
    }

    protected abstract boolean isDoStuff();


    // second way is to define methodDelegate() that will call hidden super.method()

    public void methodDelegate() {
        super.method();
    }
    ...
}

public class OneThatContainsDesiredMethod {

    public void method() {...}
    ...
}

Örneğin, uygulamanızda org.springframework.test.context.junit4.SpringJUnit4ClassRunner sınıfı oluşturabilirsiniz, böylece bu sınıf kavanozdan gerçek sınıftan önce yüklenmelidir. Ardından yöntemleri veya yapıcıları yeniden yazın.

Dikkat: Bu mutlak bir kesmek ve kullanılması şiddetle tavsiye EDİLMİYOR ama ÇALIŞIYOR! Sınıf yükleyicileriyle ilgili olası sorunlar nedeniyle bu yaklaşımın kullanılması tehlikelidir. Ayrıca, üzerine yazılan sınıfı içeren kitaplığı her güncelleştirdiğinizde bu sorunlara neden olabilir.


1

Mimari birkaç türetilmiş sınıflar adına uygulayan ortak bir CustomBaseClass ortak işlevsellik oluşturmak için böyle durumlar oldu. Ancak, belirli bir türetilmiş sınıfa özgü yöntem için ortak mantığı atlatmamız gerekir. Bu gibi durumlarda, bir super.super.methodX uygulaması kullanmalıyız.

Bunu, CustomBaseClass'a, özel uygulamayı seçici olarak ertelemek ve istenirse varsayılan çerçeve uygulamasına verim sağlamak için kullanılabilen bir boole üyesi ekleyerek başarıyoruz.

        ...
        FrameworkBaseClass (....) extends...
        {
           methodA(...){...}
           methodB(...){...}
        ...
           methodX(...)
        ...
           methodN(...){...}

        }
        /* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/
        CustomBaseClass(...) extends FrameworkBaseClass 
        {
        private boolean skipMethodX=false; 
        /* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/

           methodA(...){...}
           methodB(...){...}
        ...
           methodN(...){...}

           methodX(...){
                  if (isSkipMethodX()) {
                       setSKipMethodX(false);
                       super.methodX(...);
                       return;
                       }
                   ... //common method logic
            }
        }

        DerivedClass1(...) extends CustomBaseClass
        DerivedClass2(...) extends CustomBaseClass 
        ...
        DerivedClassN(...) extends CustomBaseClass...

        DerivedClassX(...) extends CustomBaseClass...
        {
           methodX(...){
                  super.setSKipMethodX(true);
                  super.methodX(...);
                       }
        }

Bununla birlikte, uygulamada olduğu gibi çerçeve içinde iyi mimari ilkeleri izlendiğinde, isA yaklaşımı yerine hasA yaklaşımını kullanarak bu tür durumları kolayca önleyebiliriz. Ancak her zaman iyi tasarlanmış mimariyi yerinde beklemek çok pratik değildir ve bu nedenle sağlam tasarım ilkelerinden uzaklaşmak ve bunun gibi hack'leri tanıtmak gerekir. Sadece 2 sentim ...


1

@Jon Skeet Güzel bir açıklama. IMO, super.super yöntemini çağırmak istiyorsa, o zaman hemen ebeveynin davranışını yoksaymak, ancak büyük ebeveyn davranışına erişmek istemek gerekir. Bu, Of örneği aracılığıyla elde edilebilir. Aşağıdaki gibi kod

public class A {
    protected void printClass() {
        System.out.println("In A Class");
    }
}

public class B extends A {

    @Override
    protected void printClass() {
        if (!(this instanceof C)) {
            System.out.println("In B Class");
        }
        super.printClass();
    }
}

public class C extends B {
    @Override
    protected void printClass() {
        System.out.println("In C Class");
        super.printClass();
    }
}

İşte sürücü sınıfı,

public class Driver {
    public static void main(String[] args) {
        C c = new C();
        c.printClass();
    }
}

Bunun çıktısı

In C Class
In A Class

Bu durumda B Sınıfı printClass davranışı yok sayılır. Bu super.super'e ulaşmak için ideal veya iyi bir uygulama olduğundan emin değilim, ama yine de çalışıyor.


1
Bu yaratıcı ama sorumu gerçekten cevaplamıyor. C hala süper demiyor, B sadece farklı davranıyor. A ve B'yi değiştirebiliyorsanız, instanceof kullanmak yerine başka bir yöntem ekleyebilirsiniz. Super.super.foo, A ve B'ye erişiminiz olmadığı ve bunları değiştiremediğiniz durumlarda size yardımcı olacaktır.
Tim Büthe

@TimButhe, Ama eğer biri super.super'i aramak istiyorsa o zaman kasten ebeveyn sınıfının davranışını görmezden gelmek ister, bu yüzden u sadece java'nın sözdizimi ile bu şeye ulaşmanız gerekir. (İstediğiniz herhangi bir seçenek / farklı yöntem)
Sanjay Jain

0

Üst sınıfa ihtiyacınız olacağını düşünüyorsanız, bu sınıf için bir değişkene başvurabilirsiniz. Örneğin:

public class Foo
{
  public int getNumber()
  {
    return 0;
  }
}

public class SuperFoo extends Foo
{
  public static Foo superClass = new Foo();
  public int getNumber()
  {
    return 1;
  }
}

public class UltraFoo extends Foo
{
  public static void main(String[] args)
  {
    System.out.println(new UltraFoo.getNumber());
    System.out.println(new SuperFoo().getNumber());
    System.out.println(new SuperFoo().superClass.getNumber());
  }
  public int getNumber()
  {
    return 2;
  }
}

Çıktı almalı:

2
1
0

2
Exmaple'ınız biraz ... kötü, çünkü statik yöntemler kullanıyorsunuz. Statik yöntemler kullanırken, değişkene veya super'e hiç ihtiyacınız yoktur. Belki burada bazı temel OO kavramını kaçırdınız, bunu okuduğunuzdan emin olun. Üzgünüm, aşağı inmek zorunda.
Tim Büthe

1
Statik yöntemler olmadan kolayca yapılabilirdi. Yeterince basit. Bunun nasıl yapılacağına dair basit bir örnek verecektim.
Ashtheking

Demek istediğim, süper değişkeni bir alanda saklamak bunu çözmenin bir yoludur. Ancak, statik bir yöntemse değişkene ihtiyacınız yoktur, sadece kullanabilirsiniz. İkincisi, bir değişkeni statik yöntemlerle çağırmak kötü bir uygulamadır ve çoğu IDE sizi bu konuda uyaracaktır. Cevabınızı düzeltirseniz, statik şeyleri kaldırırsanız, aşağı oyumu kaldırmaktan memnuniyet duyarım, suç yok.
Tim Büthe

Yakınız ama kodunuz derlenmeyecek. Ana yönteminizdeki statik bir bağlamdan getNumber'a erişmeye çalışın. Aslında bunu derlemeye çalıştınız mı? (ve UltraFoo'nuz SuperFoo'yu uzatmamalıdır?)
Tim Büthe

Sana acımasız olmak istemiyorum, ama new UltraFoo.getNumber()orada parantezleri kaçırdığın için derlenmeyeceğim. Ancak, dontote'umu yeni kaldırdım, çünkü kod kavramı şimdi oldukça açık, teşekkürler!
Tim Büthe

0

IMO, super.super.sayYourName()Java'da davranış elde etmenin temiz bir yoludur .

public class GrandMa {  
    public void sayYourName(){  
        System.out.println("Grandma Fedora");  
    }  
}  

public class Mama extends GrandMa {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName();  
        }else {  
            System.out.println("Mama Stephanida");  
        }  
    }  
}  

public class Daughter extends Mama {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName(lie);  
        }else {  
            System.out.println("Little girl Masha");  
        }  
    }  
}  

public class TestDaughter {
    public static void main(String[] args){
        Daughter d = new Daughter();

        System.out.print("Request to lie: d.sayYourName(true) returns ");
        d.sayYourName(true);
        System.out.print("Request not to lie: d.sayYourName(false) returns ");
        d.sayYourName(false);
    }
}

Çıktı:

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha


ah, böyle bir sınıf hiyerarşisini uygulamayı mı savunuyorsunuz? Ne yazık ki, Baby'den (Kızın alt sınıfı)
Mama'daki

yakov fain doğru. diğer bir deyişle, bu iyi bir örnek değildir, çünkü asıl soru super.super'de geçersiz kılınmış bir yöntemi çağırmaktı.
inor

0

Bunun miras anlaşmasını bozan bir sorun olduğunu düşünüyorum.
Bir sınıfı genişleterek davranışlarına, özelliklerine
uyuyorsunuz / katılıyorsunuz super.super.method().

Süper sınıftan kiraz seçemezsin .

Ancak, arama yapma gereksinimi duyduğunuz durumlar olabilir super.super.method()- genellikle kötü bir tasarım işareti, kodunuzda veya miras aldığınız kodda!
Eğer süper ve süper süper sınıfları sonra miras üzerinde kompozisyon için opt, (bazı eski kodu) refactored edilemez.

Encapsulation breaking, enkapsüle edilmiş kodu kırarak bazı yöntemleri geçersiz kıldığınızda gerçekleşir . Geçersiz kılınmaması için tasarlanan yöntemler nihai olarak işaretlenir .


0

C # 'da böyle bir atadan bir yöntem çağırabilirsiniz:

public class A
    internal virtual void foo()
...
public class B : A
    public new void foo()
...
public class C : B
    public new void foo() {
       (this as A).foo();
    }

Ayrıca bunu Delphi'de yapabilirsiniz:

type
   A=class
      procedure foo;
      ...
   B=class(A)
     procedure foo; override;
     ...
   C=class(B)
     procedure foo; override;
     ...
A(objC).foo();

Ancak Java'da böyle bir odaklamayı sadece bir dişli ile yapabilirsiniz. Olası bir yol:

class A {               
   int y=10;            

   void foo(Class X) throws Exception {  
      if(X!=A.class)
         throw new Exception("Incorrect parameter of "+this.getClass().getName()+".foo("+X.getName()+")");
      y++;
      System.out.printf("A.foo(%s): y=%d\n",X.getName(),y);
   }
   void foo() throws Exception { 
      System.out.printf("A.foo()\n");
      this.foo(this.getClass()); 
   }
}

class B extends A {     
   int y=20;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==B.class) { 
         y++; 
         System.out.printf("B.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("B.foo(%s) calls B.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }
}

class C extends B {     
   int y=30;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==C.class) { 
         y++; 
         System.out.printf("C.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("C.foo(%s) calls C.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }

   void DoIt() {
      try {
         System.out.printf("DoIt: foo():\n");
         foo();         
         Show();

         System.out.printf("DoIt: foo(B):\n");
         foo(B.class);  
         Show();

         System.out.printf("DoIt: foo(A):\n");
         foo(A.class);  
         Show();
      } catch(Exception e) {
         //...
      }
   }

   void Show() {
      System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d\n\n", ((A)this).y, ((B)this).y, ((C)this).y);
   }
} 

objC.DoIt () sonuç çıktısı:

DoIt: foo():
A.foo()
C.foo(C): y=31
Show: A.y=10, B.y=20, C.y=31

DoIt: foo(B):
C.foo(B) calls C.super.foo(B)
B.foo(B): y=21
Show: A.y=10, B.y=21, C.y=31

DoIt: foo(A):
C.foo(A) calls C.super.foo(A)
B.foo(A) calls B.super.foo(A)
A.foo(A): y=11
Show: A.y=11, B.y=21, C.y=31

C # 'da sadece sanal olmayan yöntemler için çalışır ve tüm yöntemler Java'da sanal olduğundan gerçekten farklı değildir.
Agent_L

0

Bunu yapmak kolaydır. Örneğin:

A'nın B ve B alt sınıflarının C alt sınıfları. Her ikisinin de örneğin methodName () yöntemi vardır.

public abstract class A {

    public void methodName() {
        System.out.println("Class A");
    }

}

public class B extends A {

    public void methodName() {
        super.methodName();
        System.out.println("Class B");
    }

    // Will call the super methodName
    public void hackSuper() {
        super.methodName();
    }

}

public class C extends B {

    public static void main(String[] args) {
        A a = new C();
        a.methodName();
    }

    @Override
    public void methodName() {
        /*super.methodName();*/
        hackSuper();
        System.out.println("Class C");
    }

}

Run class C Çıktı: Class A Class C

Çıktı yerine: Sınıf A Sınıf B Sınıf C


-1
public class SubSubClass extends SubClass {

    @Override
    public void print() {
        super.superPrint();
    }

    public static void main(String[] args) {
        new SubSubClass().print();
    }
}

class SuperClass {

    public void print() {
        System.out.println("Printed in the GrandDad");
    }
}

class SubClass extends SuperClass {

    public void superPrint() {
        super.print();
    }
}

Çıktı: GrandDad'de basılmıştır


2
Bu cevap sorunun kapsamı dışındadır. OP, büyükbaba veya büyükanne sınıfında bir yöntemin nasıl çağrılacağını sormadı. Sorun, super.super.method()Java'da neden geçerli kod olmadığının bir tartışmasıdır .
Jed Schaaf

-1

Süper anahtar kelimesi, üst sınıftaki yöntemi çağırmanın bir yoludur. Java eğitiminde: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Yönteminiz üst sınıfının yöntemlerinden birini geçersiz kılarsa, super anahtar sözcüğünü kullanarak superridden yöntemini çağırabilirsiniz.

Süper nesnenin bir referans olduğuna inanmayın !!! Hayır, yalnızca üst sınıftaki yöntemleri çağırmak için bir anahtar kelime.

İşte bir örnek:

class Animal {
    public void doSth() {
        System.out.println(this);   // It's a Cat! Not an animal!
        System.out.println("Animal do sth.");
    }
}

class Cat extends Animal {
    public void doSth() {
        System.out.println(this);
        System.out.println("Cat do sth.");
        super.doSth();
    }
}

Aradığınızda cat.doSth(), doSth()sınıftaki yöntem Animalyazdırılır thisve bir kedidir.

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.