Java: Geçersiz kılınmış bir yöntemi çağıran bir süper yöntemi çağırmak


98
public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

beklediğim çıktı:

alt sınıf yöntemi1
üst
sınıf yöntemi1 üst sınıf yöntemi2

gerçek çıktı:

alt sınıf yöntemi1
üst
sınıf yöntemi1 alt sınıf yöntemi2

Teknik olarak kamuya açık bir yöntemi geçersiz kıldığımı biliyorum, ama anladım ki, süper'i aradığım için, süper içindeki herhangi bir çağrının süperlikte kalacağını, bu olmuyor. Bunu nasıl gerçekleştirebileceğime dair herhangi bir fikrin var mı?


2
"Miras yerine kompozisyonu tercih etmek" isteyebileceğinizden şüpheleniyorum.
Tom Hawtin - tackline

Yanıtlar:


84

Anahtar kelime super"yapışmaz". Her yöntem çağrısı ayrı ayrı ele alınır, bu nedenle SuperClass.method1()arayarak yapsanız bile super, bu gelecekte yapabileceğiniz diğer yöntem çağrılarını etkilemez.

Bunun anlamı aramak için doğrudan bir yol yoktur SuperClass.method2()dan SuperClass.method1()olsa gitmeden SubClass.method2()sen gerçek bir örneğiyle çalışmadığınız sürece SuperClass.

Yansıma kullanarak istenen efekti bile elde edemezsiniz ( belgelerinejava.lang.reflect.Method.invoke(Object, Object...) bakın ).

[DÜZENLE] Hala bazı karışıklıklar var gibi görünüyor. Farklı bir açıklama deneyeyim.

Çağırdığınızda foo(), aslında çağırırsınız this.foo(). Java, yalnızca this. Söz konusu örnekte, türü thisolduğunu SubClass.

Böylece, Java kodu içinde çalıştırdığında, SuperClass.method1()sonunda şu adrese ulaşır:this.method2();

Kullanmak super, işaret edilen örneği değiştirmez this. Yani çağrı gider SubClass.method2()beri thistiptedir SubClass.

Belki Java'nın thisgizli bir ilk parametre olarak geçtiğini hayal ettiğinizde anlamak daha kolaydır :

public class SuperClass
{
    public void method1(SuperClass this)
    {
        System.out.println("superclass method1");
        this.method2(this); // <--- this == mSubClass
    }

    public void method2(SuperClass this)
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1(SubClass this)
    {
        System.out.println("subclass method1");
        super.method1(this);
    }

    @Override
    public void method2(SubClass this)
    {
        System.out.println("subclass method2");
    }
}



public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1(mSubClass);
    }
}

Çağrı yığınını takip ederseniz, thisasla değişmediğini görebilirsiniz, her zaman içinde oluşturulan örnektir main().


birisi yığından geçerken bunun (amaçlanan) bir diyagramını yükleyebilir mi? şimdiden teşekkürler!
laycat

2
@laycat: Bir diyagrama gerek yok. Java'nın "hafızası" olmadığını unutmayın super. Bir yöntemi her çağırdığında, örnek türüne bakar ve ne sıklıkta çağırırsanız çağırın, bu türle yöntemi aramaya başlar super. Yani method2bir örneğini çağırdığınızda , SubClassher zaman SubClassilk gelen birini görecektir .
Aaron Digulla

@AaronDigulla, "Java'nın süper için hafızası yok" hakkında daha fazla açıklama yapabilir misiniz?
MengT

@ Truman'sworld: cevabımda söylediğim gibi: kullanmak durumu superdeğiştirmez. "Şu andan itibaren, tüm yöntem çağrıları kullanılmaya başlanmalıdır" bazı gizli alanlar ayarlamaz SuperClass. Ya da başka bir deyişle: Değeri thisdeğişmez.
Aaron Digulla

@AaronDigulla, yani bu, süper anahtar kelimenin aslında süper sınıfa gitmek yerine alt sınıfta miras alınan yöntemleri çağırdığı anlamına mı geliyor?
MengT

15

Geçersiz kılınan yöntemlere yalnızca geçersiz kılma yöntemlerinde (veya geçersiz kılan sınıfın diğer yöntemlerinde) erişebilirsiniz.

Yani: geçersiz kılma method2()veya super.method2()geçersiz kılınan sürümün içinde arama yapmayın .


8

this" this.method2();Kullandığınız nesnenin şu anda çalışan örneğine" atıfta bulunan anahtar kelimeyi kullanıyorsunuz, yani, üst sınıfınızda çağırıyorsunuz, yani bu, kullandığınız nesnede method2 () 'yi çağıracak. SubClass olan yeniden kullanılıyor.


9
doğru ve kullanmamak thisda yardımcı olmaz. Niteliksiz bir çağrı örtük olarak kullanırthis
Sean Patrick Floyd

3
Neden buna olumlu oy veriliyor? Bu sorunun cevabı bu değil. Yazdığınızda method2()derleyici görecektir this.method2(). Yani, kaldırsanız bile thishala çalışmayacaktır. @ Sean Patrick Floyd'un söylediği doğru
Shervin Asgari

4
@Shervin yanlış bir şey söylemiyor, dışarı çıkarsan ne olacağını netleştirmiyorthis
Sean Patrick Floyd

4
Cevap, this"somut çalışan örnek sınıfına" (çalışma zamanında bilinir) atıfta bulunduğunu ve (posterin inandığı gibi) "mevcut derleme birimi sınıfına" (anahtar kelimenin kullanıldığı, Derleme zamanı). Ancak yanıltıcı da olabilir (Shervin'in belirttiği gibi): thisdüz yöntem çağrısı ile örtük olarak da başvurulur; method2();ile aynıthis.method2();
leonbloy

7

Bu şekilde düşünüyorum

+----------------+
|     super      |
+----------------+ <-----------------+
| +------------+ |                   |
| |    this    | | <-+               |
| +------------+ |   |               |
| | @method1() | |   |               |
| | @method2() | |   |               |
| +------------+ |   |               |
|    method4()   |   |               |
|    method5()   |   |               |
+----------------+   |               |
    We instantiate that class, not that one!

Alt sınıfı biraz sola kaydırıp, altında ne olduğunu ortaya çıkarayım ... (Adamım, ASCII grafiklerini seviyorum)

We are here
        |
       /  +----------------+
      |   |     super      |
      v   +----------------+
+------------+             |
|    this    |             |
+------------+             |
| @method1() | method1()   |
| @method2() | method2()   |
+------------+ method3()   |
          |    method4()   |
          |    method5()   |
          +----------------+

Then we call the method
over here...
      |               +----------------+
 _____/               |     super      |
/                     +----------------+
|   +------------+    |    bar()       |
|   |    this    |    |    foo()       |
|   +------------+    |    method0()   |
+-> | @method1() |--->|    method1()   | <------------------------------+
    | @method2() | ^  |    method2()   |                                |
    +------------+ |  |    method3()   |                                |
                   |  |    method4()   |                                |
                   |  |    method5()   |                                |
                   |  +----------------+                                |
                   \______________________________________              |
                                                          \             |
                                                          |             |
...which calls super, thus calling the super's method1() here, so that that
method (the overidden one) is executed instead[of the overriding one].

Keep in mind that, in the inheritance hierarchy, since the instantiated
class is the sub one, for methods called via super.something() everything
is the same except for one thing (two, actually): "this" means "the only
this we have" (a pointer to the class we have instantiated, the
subclass), even when java syntax allows us to omit "this" (most of the
time); "super", though, is polymorphism-aware and always refers to the
superclass of the class (instantiated or not) that we're actually
executing code from ("this" is about objects [and can't be used in a
static context], super is about classes).

Başka bir deyişle, Java Dil Spesifikasyonundan alıntı yapmak :

Form , geçerli nesnenin super.Identifieradlandırılmış Identifier, ancak geçerli nesnenin geçerli sınıfın üst sınıfının bir örneği olarak görüldüğü alana başvurur.

Form T.super.Identifier, Identifierkarşılık gelen sözcüksel olarak çevreleyen örneğe karşılık gelen T, ancak bu örnek üst sınıfının bir örneği olarak görülen alana başvurur T.

Layman'ın terimleriyle, thistemelde bir nesnedir (* ** nesne; değişkenler içinde hareket ettirebileceğiniz aynı nesne), başlatılan sınıfın örneği, veri alanında düz bir değişkendir; superçalıştırılmasını istediğiniz ödünç alınmış bir kod bloğunun işaretçisi gibidir, daha çok bir işlev çağrısı gibidir ve çağrıldığı sınıfa göre değişir.

Bu nedenle super, süper sınıftan kullanırsanız , süper sınıftan [büyükbaba] çalıştırılan) kod alırsınız, ancak bir üst sınıftan kullanırsanız this(veya örtük olarak kullanılıyorsa) alt sınıfa işaret etmeye devam eder (çünkü onu kimse değiştirmedi - ve hiç kimse abilir).


3
class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        SuperClass se=new SuperClass();
        se.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }
}


class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

arama

SubClass mSubClass = new SubClass();
mSubClass.method1();

çıktılar

alt sınıf yöntemi1
üst
sınıf yöntemi1 üst sınıf yöntemi2


2

SuperClass.method1'in subClass.method2'yi çağırmasını istemiyorsanız, method2'yi özel yapın, böylece geçersiz kılınamaz.

İşte bir öneri:

public class SuperClass {

  public void method1() {
    System.out.println("superclass method1");
    this.internalMethod2();
  }

  public void method2()  {
    // this method can be overridden.  
    // It can still be invoked by a childclass using super
    internalMethod2();
  }

  private void internalMethod2()  {
    // this one cannot.  Call this one if you want to be sure to use
    // this implementation.
    System.out.println("superclass method2");
  }

}

public class SubClass extends SuperClass {

  @Override
  public void method1() {
    System.out.println("subclass method1");
    super.method1();
  }

  @Override
  public void method2() {
    System.out.println("subclass method2");
  }
}

Bu şekilde çalışmasaydı, polimorfizm imkansız olurdu (veya en azından bunun yarısı kadar bile).


2

Bir yöntemi geçersiz kılmaktan kaçınmanın tek yolu super anahtar kelimesini kullanmak olduğundan , method2 () ' yi SuperClass'tan başka bir yeni Base sınıfına taşımayı ve ardından onu SuperClass'tan çağırmayı düşündüm :

class Base 
{
    public void method2()
    {
        System.out.println("superclass method2");
    }
}

class SuperClass extends Base
{
    public void method1()
    {
        System.out.println("superclass method1");
        super.method2();
    }
}

class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

Çıktı:

subclass method1
superclass method1
superclass method2

2

this her zaman şu anda çalışan nesneyi ifade eder.

Buradaki noktayı daha fazla açıklamak için basit bir taslak:

+----------------+
|  Subclass      |
|----------------|
|  @method1()    |
|  @method2()    |
|                |
| +------------+ |
| | Superclass | |
| |------------| |
| | method1()  | |
| | method2()  | |
| +------------+ |
+----------------+

Dış kutunun bir örneğine sahipseniz, bir Subclass nesneye sahipseniz, kutunun içinde, hatta Superclass'alana' girdiğiniz her yerde , bu yine de dış kutunun örneğidir.

Dahası, bu programda üç sınıftan yaratılan yalnızca bir nesne vardır, bu nedenle thisyalnızca bir şeye atıfta bulunabilir ve o:

görüntü açıklamasını buraya girin

Netbeans 'Heap Walker'da gösterildiği gibi .


1

Bunu doğrudan yapabileceğine inanmıyorum. Bir geçici çözüm, üst sınıfta method2'nin özel bir dahili uygulamasına sahip olmak ve bunu aramak olabilir. Örneğin:

public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.internalMethod2();
    }

    public void method2()
    {
        this.internalMethod2(); 
    }
    private void internalMethod2()
    {
        System.out.println("superclass method2");
    }

}

1

"this" anahtar sözcüğü geçerli sınıf referansını ifade eder. Bu, yöntemin içinde kullanıldığında, 'geçerli' sınıfın hala Alt Sınıf olduğu ve dolayısıyla yanıtın açıklandığı anlamına gelir.


1

Özetlemek gerekirse, bu mevcut nesneyi işaret eder ve java'daki yöntem çağrısı, doğası gereği çok biçimlidir. Dolayısıyla, yürütme için yöntem seçimi tamamen bununla işaret edilen nesneye bağlıdır. Bu nedenle, üst sınıftan method2 () yöntemini çağırmak, alt sınıfın method2 () yöntemini çağırır, çünkü bu alt sınıfın nesnesini işaret eder. Bunun tanımı, hangi sınıfın kullanıldığına bakılmaksızın değişmez.

PS. yöntemlerden farklı olarak, sınıfın üye değişkenleri polimorfik değildir.


0

Benzer bir vaka için araştırmam sırasında, aramanın nereden geldiğini bulmak için alt sınıf yönteminde yığın izini kontrol ederek sonlandırıyorum. Muhtemelen bunu yapmanın daha akıllıca yolları vardır, ama benim için işe yarıyor ve dinamik bir yaklaşım.

public void method2(){
        Exception ex=new Exception();
        StackTraceElement[] ste=ex.getStackTrace();
        if(ste[1].getClassName().equals(this.getClass().getSuperclass().getName())){
            super.method2();
        }
        else{
            //subclass method2 code
        }
}

Davaya bir çözüm bulmanın mantıklı olduğunu düşünüyorum. İş parçacığında daha önce bahsedildiği gibi, sorunu farklı yöntem adları veya hatta farklı parametre türleri ile çözmenin elbette yolları vardır, ancak benim durumumda farklı yöntem adlarıyla karıştırmayı sevmiyorum.


Bu kod tehlikeli, riskli ve pahalıdır. İstisnaların oluşturulması, sanal makinenin tam bir yığın izleme oluşturmasını gerektirir, tam imzayı değil, yalnızca adı karşılaştırmak hataya açıktır. Ayrıca, büyük bir tasarım hatası kokuyor.
M. le Rutte

Performans açısından, kodum 'yeni HashMap (). Size ()' den daha fazla etki üretmiyor gibi görünüyor. Ancak, düşündüğünüz endişeleri gözden kaçırmış olabilirim ve ben ücret karşılığında bir VM uzmanı değilim. Şüphenizi sınıf isimlerini karşılaştırdığımda görüyorum, ama bu beni oldukça emin kılan paketi içeriyor, bu benim ebeveyn sınıfım. Her neyse, bunun yerine imzayı karşılaştırma fikrini seviyorum, bunu nasıl yapardınız? Genel olarak, arayanın süper sınıf mı yoksa başka biri mi olduğunu belirlemenin daha sorunsuz bir yolu varsa, burada minnettar olurum.
Siegrist'i

Arayanın süper bir sınıf olup olmadığını belirlemeniz gerekiyorsa, yeniden tasarım yapılırsa daha uzun süre düşünürüm. Bu bir anti-modeldir.
M. le Rutte

Konuyu görüyorum ama ipliğin genel isteği makul. Bazı durumlarda, herhangi bir iç içe geçmiş yöntem çağrısıyla bir üst sınıf yöntem çağrısının üst sınıf bağlamında kalması mantıklı olabilir. Bununla birlikte, Superclass'ta yöntem çağrısını buna göre yönlendirmenin bir yolu yok gibi görünüyor.
Siegrist'i

0

Ortaya çıkan sorunun çıktısını daha da genişleterek, bu, erişim belirticisi ve geçersiz kılma davranışı hakkında daha fazla fikir verecektir.

            package overridefunction;
            public class SuperClass 
                {
                public void method1()
                {
                    System.out.println("superclass method1");
                    this.method2();
                    this.method3();
                    this.method4();
                    this.method5();
                }
                public void method2()
                {
                    System.out.println("superclass method2");
                }
                private void method3()
                {
                    System.out.println("superclass method3");
                }
                protected void method4()
                {
                    System.out.println("superclass method4");
                }
                void method5()
                {
                    System.out.println("superclass method5");
                }
            }

            package overridefunction;
            public class SubClass extends SuperClass
            {
                @Override
                public void method1()
                {
                    System.out.println("subclass method1");
                    super.method1();
                }
                @Override
                public void method2()
                {
                    System.out.println("subclass method2");
                }
                // @Override
                private void method3()
                {
                    System.out.println("subclass method3");
                }
                @Override
                protected void method4()
                {
                    System.out.println("subclass method4");
                }
                @Override
                void method5()
                {
                    System.out.println("subclass method5");
                }
            }

            package overridefunction;
            public class Demo 
            {
                public static void main(String[] args) 
                {
                    SubClass mSubClass = new SubClass();
                    mSubClass.method1();
                }
            }

            subclass method1
            superclass method1
            subclass method2
            superclass method3
            subclass method4
            subclass method5
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.