Mengapa super.super.method (); tidak diizinkan di Jawa?

360

Saya membaca pertanyaan ini dan berpikir bahwa itu akan dengan mudah diselesaikan (bukan karena itu tidak dapat dipecahkan tanpa) jika seseorang dapat menulis:

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

Saya tidak yakin apakah itu berguna dalam banyak kasus, tetapi saya bertanya - tanya mengapa itu tidak berguna dan jika sesuatu seperti ini ada dalam bahasa lain.

apa yang kalian pikirkan?

EDIT: Untuk memperjelas: ya saya tahu, itu tidak mungkin di Jawa dan saya tidak benar-benar melewatkannya. Ini bukan apa-apa yang saya harapkan untuk bekerja dan terkejut mendapatkan kesalahan kompiler. Saya hanya punya ide dan suka mendiskusikannya.

Tim Büthe
sumber
7
Ingin menelepon super.super.toString()bertentangan dengan keputusan Anda sendiri ketika Anda memilih untuk memperpanjang kelas sehingga menerima semua (bukan sebagian) fitur-fiturnya.
DayaMoon

Jawaban:

484

Itu melanggar enkapsulasi. Anda seharusnya tidak dapat mem-bypass perilaku kelas induk. Masuk akal untuk kadang-kadang dapat mem-bypass perilaku kelas Anda sendiri (khususnya dari dalam metode yang sama) tetapi tidak dengan perilaku orang tua Anda. Sebagai contoh, misalkan kita memiliki basis "koleksi item", subkelas yang mewakili "koleksi item merah" dan subkelas yang mewakili "koleksi item merah besar". Masuk akal untuk memiliki:

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);
    }
}

Tidak apa-apa - RedItems selalu dapat yakin bahwa item yang dikandungnya semuanya berwarna merah. Sekarang anggaplah kita berada dapat memanggil super.super.add ():

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);
    }
}

Sekarang kita bisa menambahkan apa pun yang kita suka, dan invarian di RedItemsrusak.

Apakah itu masuk akal?

Jon Skeet
sumber
38
contoh yang bagus. tapi saya pikir itu desain yang buruk ketika kelas dasar menerima item, tetapi kelas turunan menolaknya, karena kelas turunan tidak dapat digunakan sebagai pengganti drop-in untuk kelas-dasar (pelanggaran prinsip substitusi). apakah itu pemikiran yang benar, atau apakah hierarki seperti itu bersih?
Johannes Schaub - litb
5
Apakah maksud Anda komposisi lebih dari warisan? Contoh ini bukan alasan untuk menghindari warisan - meskipun ada banyak yang lain.
Jon Skeet
3
@Tim: Ini hanya contoh yang mudah dimengerti tentang melanggar enkapsulasi. Bisa jadi malah mengatur properti. Selain itu, tidak semua aspek akan terlihat pada level tipe. Obat generik bukanlah jawaban untuk segalanya.
Jon Skeet
12
@ JohannesSchaub-litb Saya pikir itu melanggar prinsip substitusi Liskov, karena jika seseorang mengkode kontrak Item dan menggunakan instance RedItems, orang akan mendapatkan NotRedItemException yang tidak terduga. Saya selalu diajarkan bahwa subkelas harus mengambil set input super dan mengembalikan subset output. Dengan kata lain, sebuah sub kelas tidak boleh menolak input yang valid untuk kelas super atau menghasilkan output yang tidak bisa dihasilkan oleh kelas super, ini termasuk melempar kesalahan yang tidak dibuang oleh kelas super.
Konstantin Tarashchanskiy
4
@piechuckerr: Terkadang buku, terkadang posting blog, terkadang hanya pengalaman ...
Jon Skeet
72

Saya pikir Jon Skeet memiliki jawaban yang benar. Saya hanya ingin menambahkan bahwa Anda dapat mengakses variabel gelap dari superclasses superclasses dengan melemparkan 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();
        }
}

yang menghasilkan output:

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

(contoh dari JLS )

Namun, ini tidak berfungsi untuk pemanggilan metode karena pemanggilan metode ditentukan berdasarkan tipe runtime objek.

Michael Myers
sumber
6
Jika Anda memiliki variabel nama yang sama dengan satu di superclass dan karena alasan tertentu Anda tidak dapat (atau tidak diizinkan) mengubahnya, Anda masih dapat mengakses variabel superclass dengan nama yang sama. Apa gunanya, Anda bertanya? Yah ... saya tidak pernah menggunakannya.
Michael Myers
Tautan bagus ke dokumen ekspresi. Beberapa contoh bagus untuk orang yang belajar untuk OCPJP.
Gordon
Luar biasa. Saya tidak akan pernah berpikir untuk menggunakan gips.
Thomas Eding
kenapa kelas T1 menimpa variabel x? final statis secara default kan?
amarnath harish
@amarnathharish: Ini tidak diganti, dan bidang secara default dilindungi paket.
Michael Myers
41

Saya pikir kode berikut memungkinkan untuk menggunakan super.super ... super.method () dalam banyak kasus. (Bahkan jika itu buruk untuk melakukannya)

Pendeknya

  1. buat instance sementara dari tipe leluhur
  2. salin nilai bidang dari objek asli ke objek sementara
  3. aktifkan metode target pada objek sementara
  4. salin nilai yang dimodifikasi kembali ke objek asli

Penggunaan:

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);
    }
}
Nico
sumber
10
Dengan refleksi Anda dapat melakukan apa saja, ya :) Anda bahkan dapat membuat string bisa berubah.
BalusC
23
Sungguh hal yang mengerikan untuk dilakukan! Saya memberi Anda +1 hanya untuk mencari tahu bagaimana melakukannya sama sekali :)
Larry Watanabe
6
Ini adalah trik yang bagus tetapi bahkan itu tidak selalu setara dengan memanggil super.super yang tidak dapat dihindari, namun diperlukan. Dan itu karena panggilan super.super akan membawa konteks C (C + B + A) sedangkan jawaban Anda menciptakan sebuah instance dari A tanpa konteks B dan C. Jadi jawaban ini tidak akan berfungsi jika setiap doThat disebut getContext (), misalnya, dan getContext diimplementasikan secara berbeda di setiap kelas. dalam jawaban Anda itu akan menggunakan getContext () sedangkan memanggil super.super tidak tersedia akan menghasilkan menggunakan getContext C.
inor
Hmm. Dalam beberapa kasus, mungkin Anda dapat mengatasi keberatan inor dengan proksi dinamis ( javahowto.blogspot.co.uk/2011/12/12/... ), mengarahkan kembali panggilan metode ke objek asli (menyinkronkan variabel setelah setiap panggilan)? Tampaknya proksi membutuhkan semuanya untuk mengimplementasikan antarmuka. Juga, saya bertanya-tanya apakah mungkin bagi kelas super-super untuk memanggil salah satu metode super-supernya, khususnya, dan Anda tidak perlu untuk mengarahkan ulang mereka ....
Erhannis
Saya mengatakan dalam komentar lain bahwa memblokir super.super.mengundang programmer untuk menemukan cara-cara baru, berbelit-belit dan mengerikan untuk menembak diri sendiri dalam upaya mencari solusi, ini adalah contoh sempurna dari itu, karena rekan kerja Anda mungkin akan sangat membenci Anda karena menulis sesuatu seperti ini mereka akan menembak Anda secara pribadi dan secara harfiah di kaki. +1
Braden Best
11

Saya tidak memiliki reputasi yang cukup untuk berkomentar sehingga saya akan menambahkan ini ke jawaban lain.

Jon Skeet menjawab dengan sangat baik, dengan contoh yang indah. Matt B ada benarnya: tidak semua superclasses memiliki supers. Kode Anda akan rusak jika Anda memanggil super dari super yang tidak memiliki super.

Pemrograman berorientasi objek (yang Java) adalah semua tentang objek, bukan fungsi. Jika Anda ingin pemrograman berorientasi tugas, pilih C ++ atau yang lainnya. Jika objek Anda tidak sesuai dengan kelas supernya, maka Anda perlu menambahkannya ke "kelas kakek-nenek", membuat kelas baru, atau menemukan super lain yang cocok dengannya.

Secara pribadi, saya menemukan keterbatasan ini sebagai salah satu kekuatan terbesar Jawa. Kode agak kaku dibandingkan dengan bahasa lain yang saya gunakan, tetapi saya selalu tahu apa yang diharapkan. Ini membantu dengan tujuan "sederhana dan akrab" Jawa. Dalam pikiran saya, memanggil super.super tidak sederhana atau akrab. Mungkin para pengembang merasakan hal yang sama?

EllaJo
sumber
3
Anda mengatakan "tidak semua superclasses memiliki supers". Yah, semua kecuali java.lang.Object apa yang bisa memberi Anda "null". Jadi saya akan mengatakan, hampir semua memiliki makan malam.
Tim Büthe
Setiap kelas yang ditulis oleh pemrogram aplikasi memiliki "super" (java.lang.Object tidak, tetapi pemrogram aplikasi tidak menulisnya.)
finnw
2
Mudah dipecahkan dengan membuat super.super.super ... super kesalahan waktu kompilasi jika ada terlalu banyak supers. Mengingat Java hanya memiliki warisan publik, jika seseorang mengubah hierarki warisan, Anda mengubah antarmuka. Jadi saya tidak akan khawatir bahwa super menakutkan.
Thomas Eding
7

Ada beberapa alasan bagus untuk melakukan ini. Anda mungkin memiliki subkelas yang memiliki metode yang diterapkan secara salah, tetapi metode induk diimplementasikan dengan benar. Karena itu milik perpustakaan pihak ketiga, Anda mungkin tidak dapat / tidak mau mengubah sumbernya. Dalam hal ini, Anda ingin membuat subkelas tetapi mengganti satu metode untuk memanggil metode super.super.

Seperti yang ditunjukkan oleh beberapa poster lain, dimungkinkan untuk melakukan ini melalui refleksi, tetapi harus mungkin untuk melakukan sesuatu seperti

(SuperSuperClass ini) .theMethod ();

Saya sedang menangani masalah ini sekarang - perbaikan cepatnya adalah dengan menyalin dan menempelkan metode superclass ke dalam metode sub-kelas :)

Larry Watanabe
sumber
1
Casting sebelum memanggil metode tidak mengubah metode yang didelegasikan ke — Selalu implementasi subclass yang digunakan, tidak pernah super itu. Lihat, misalnya, stackoverflow.com/questions/1677993/…
Joshua Goldberg
1
@Larry ini persis situasi yang saya alami, dan persis perbaikan yang saya gunakan. Panggilan bagus!
BC
Jika Anda memiliki kode metode yang diperlukan, Anda dapat membuka semua bidang yang diperlukan dan menjalankan kode ini di subkelas Anda.
Enyby
6

Selain poin yang sangat baik yang telah dibuat orang lain, saya pikir ada alasan lain: bagaimana jika superclass tidak memiliki superclass?

Karena setiap kelas secara alami meluas (setidaknya) Object, super.whatever()akan selalu merujuk pada metode dalam superclass. Tetapi bagaimana jika kelas Anda hanya meluas Object- apa yang akan super.superdirujuk kemudian? Bagaimana seharusnya perilaku itu ditangani - kesalahan kompiler, NullPointer, dll?

Saya pikir alasan utama mengapa ini tidak diperbolehkan adalah karena melanggar enkapsulasi, tetapi ini mungkin juga merupakan alasan kecil.

matt b
sumber
4
Jelas, itu akan menjadi kesalahan kompiler - dan itu informasi yang dimiliki oleh kompiler.
Michael Borgwardt
4

Saya pikir jika Anda menimpa metode dan ingin semua versi super-kelas itu (seperti, katakan untuk equals), maka Anda hampir selalu ingin memanggil versi superclass langsung terlebih dahulu, yang mana akan memanggil versi superclass pada gilirannya jika ia menginginkannya. .

Saya pikir itu jarang masuk akal (jika sama sekali. Saya tidak bisa memikirkan kasus di mana ia melakukannya) untuk memanggil versi superclass sewenang-wenang dari suatu metode. Saya tidak tahu apakah itu mungkin sama sekali di Jawa. Itu bisa dilakukan di C ++:

this->ReallyTheBase::foo();
Johannes Schaub - litb
sumber
6
Masuk akal jika seseorang menulis subclass dengan satu metode yang diimplementasikan secara tidak benar tetapi metode superclass melakukan 90% pekerjaan. Kemudian Anda ingin membuat subclass, dan mengganti metode memanggil metode superclass superclass, dan tambahkan 10% Anda sendiri.
Larry Watanabe
3

Di tebak, karena tidak sering digunakan. Satu-satunya alasan saya bisa melihatnya menggunakannya adalah jika orang tua langsung Anda telah menimpa beberapa fungsi dan Anda mencoba mengembalikannya kembali ke aslinya.

Yang menurut saya bertentangan dengan prinsip-prinsip OO, karena orang tua langsung kelas harus lebih dekat dengan kelas Anda daripada kakek-nenek.

Powerlord
sumber
3

Lihatlah ini proyek Github, terutama variabel objectHandle. Proyek ini menunjukkan bagaimana memanggil metode kakek-nenek secara aktual dan akurat.

Untuk berjaga-jaga jika tautannya rusak, berikut adalah kodenya:

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";
        }
    }
}

Selamat Coding !!!!

kyay
sumber
Para ilmuwan Anda begitu sibuk dengan apakah mereka bisa atau tidak, mereka tidak berhenti untuk berpikir apakah mereka harus melakukannya. Tolong jangan benar-benar melakukan ini ...: P 🤔
Tim Büthe
Ya, itu kata-kata si pembuat kode, bukan milikku. Menurut pendapat saya, saya pikir Anda mungkin benar-benar membutuhkannya suatu hari nanti
kyay
2

Saya akan meletakkan tubuh metode super.super di metode lain, jika mungkin

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();
    }
}

Atau jika Anda tidak dapat mengubah kelas super-super, Anda dapat mencoba ini:

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;
    }
}

Dalam kedua kasus tersebut,

new ChildClass().toString();

hasil untuk "Saya super super"

xMichal
sumber
Saya baru saja menemukan diri saya dalam situasi di mana saya memiliki SuperSuperClass dan ChildClass tetapi tidak SuperClass, jadi saya menemukan solusi pertama yang berguna.
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: A BUILD SUCCESSFUL (total waktu: 0 detik)

Boris
sumber
1
Saya mengerti, tetapi Anda membuat contoh baru sehingga ini tidak akan berfungsi jika objek tersebut memiliki status apa pun.
Tim Büthe
1

Memanggil super.super.method () masuk akal ketika Anda tidak dapat mengubah kode kelas dasar. Ini sering terjadi ketika Anda memperluas perpustakaan yang ada.

Tanyakan kepada diri Anda terlebih dahulu, mengapa Anda memperluas kelas itu? Jika jawabannya "karena saya tidak bisa mengubahnya" maka Anda dapat membuat paket dan kelas yang tepat di aplikasi Anda, dan menulis ulang metode nakal atau membuat delegasi:

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() {...}
    ...
}

Misalnya, Anda dapat membuat kelas org.springframework.test.context.junit4.SpringJUnit4ClassRunner di aplikasi Anda sehingga kelas ini harus dimuat sebelum yang asli dari toples. Kemudian tulis ulang metode atau konstruktor.

Perhatian: Ini adalah retasan absolut, dan sangat TIDAK disarankan untuk digunakan tetapi BEKERJA! Penggunaan pendekatan ini berbahaya karena kemungkinan masalah dengan pemuat kelas. Ini juga dapat menyebabkan masalah setiap kali Anda akan memperbarui perpustakaan yang berisi kelas yang ditimpa.

ruruskyi
sumber
1

Saya memiliki situasi seperti ini ketika arsitekturnya membangun fungsionalitas umum dalam CustomBaseClass umum yang mengimplementasikan atas nama beberapa kelas turunan. Namun, kita perlu mengelak dari logika umum untuk metode khusus untuk kelas turunan tertentu. Dalam kasus seperti itu, kita harus menggunakan implementasi super.super.methodX.

Kami mencapai ini dengan memperkenalkan anggota boolean di CustomBaseClass, yang dapat digunakan untuk secara selektif menunda implementasi kustom dan menghasilkan implementasi kerangka kerja default jika diinginkan.

        ...
        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(...);
                       }
        }

Namun, dengan prinsip arsitektur yang baik diikuti dalam kerangka kerja serta aplikasi, kita dapat menghindari situasi seperti itu dengan mudah, dengan menggunakan pendekatan hasA, bukan pendekatan isA. Tetapi setiap saat itu tidak terlalu praktis untuk mengharapkan arsitektur yang dirancang dengan baik di tempat, dan karenanya perlu untuk menjauh dari prinsip-prinsip desain yang solid dan memperkenalkan hacks seperti ini. Hanya 2 sen saya ...

Ganesh Iyer
sumber
1

@ Jon Skeet Penjelasan yang bagus. IMO jika seseorang ingin memanggil metode super.super maka seseorang harus ingin mengabaikan perilaku orangtua langsung, tetapi ingin mengakses perilaku grand parent. Ini dapat dicapai melalui contoh Dari. Seperti kode di bawah ini

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();
    }
}

Ini kelas pengemudi,

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

Output dari ini akan menjadi

In C Class
In A Class

Perilaku printClass kelas B akan diabaikan dalam kasus ini. Saya tidak yakin apakah ini praktik yang ideal atau bagus untuk mencapai super.super, tetapi masih berfungsi.

Sanjay Jain
sumber
1
Yah, itu kreatif tetapi tidak benar-benar menjawab pertanyaan saya. C masih tidak memanggil super.super, B hanya berperilaku berbeda. Jika Anda dapat mengubah A dan B Anda bisa menambahkan metode lain alih-alih menggunakan instanceof. Super.super.foo akan membantu Anda dalam kasus di mana Anda tidak memiliki akses ke A dan B dan tidak dapat mengubahnya.
Tim Büthe
Setuju @TimButhe, Tetapi jika seseorang ingin memanggil super.super maka dia sengaja ingin mengabaikan perilaku kelas induk, jadi Anda hanya perlu mencapai hal itu dengan sintaks java yang ada. (Opsi apa pun yang Anda inginkan, misalnya, dari / metode yang berbeda)
Sanjay Jain
0

Jika Anda pikir Anda akan membutuhkan superclass, Anda bisa mereferensikannya dalam variabel untuk kelas itu. Sebagai contoh:

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;
  }
}

Harus mencetak:

2
1
0
Ashtheking
sumber
2
Exmaple Anda agak ... sangat buruk, karena Anda menggunakan metode statis. Saat menggunakan metode statis, Anda tidak perlu variabel atau super sama sekali. Mungkin Anda melewatkan beberapa konsep OO dasar di sini, pastikan Anda membaca tentang itu. Harus downvote, maaf.
Tim Büthe
1
Itu bisa dengan mudah dilakukan tanpa metode statis. Cukup sederhana. Saya akan contoh sederhana tentang bagaimana melakukan ini.
Ashtheking
Saya mengerti maksud Anda, menyimpan variabel super dalam suatu bidang adalah salah satu cara untuk menyelesaikannya. Tapi, Anda tidak perlu variabel jika itu adalah metode statis, Anda bisa menggunakannya. Kedua, menjalankan metode statis satu variabel adalah praktik yang buruk dan sebagian besar IDE akan memperingatkan Anda tentang hal itu. Jika Anda memperbaiki jawaban Anda, menghapus hal-hal statis, saya akan dengan senang hati menghapus downvote saya, jangan tersinggung.
Tim Büthe
Anda dekat, tetapi kode Anda tidak dapat dikompilasi. Anda mencoba mengakses getNumber dari konteks statis di metode utama Anda. Apakah Anda benar-benar mencoba untuk mengkompilasi ini? (dan seharusnya UltraFoo Anda tidak memperpanjang SuperFoo?)
Tim Büthe
Aku benar-benar tidak ingin menjadi kejam bagimu, tetapi new UltraFoo.getNumber()tidak mau mengkompilasi, karena kamu merindukan tanda kurung di sana. Namun, saya baru saja menghapus donvote saya, karena konsep kode Anda cukup jelas sekarang, terima kasih!
Tim Büthe
0

IMO, ini cara bersih untuk mencapai super.super.sayYourName()perilaku di Jawa.

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);
    }
}

Keluaran:

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

Yakov Fain
sumber
ah, jadi Anda menganjurkan penerapan hierarki kelas seperti ini? Sayangnya, ini mulai menjadi sangat berantakan jika Anda ingin mengakses metode dalam Mama dari Baby (subclass Daughter's) ...
bcr
yakov fain benar. dengan kata lain, itu bukan contoh yang baik, karena pertanyaan aslinya adalah tentang memanggil metode yang ditimpa di super.super.
inor
0

Saya pikir ini adalah masalah yang melanggar perjanjian warisan.
Dengan memperluas kelas Anda mematuhi / menyetujui perilakunya, fitur
Sementara saat menelepon super.super.method(), Anda ingin melanggar perjanjian kepatuhan Anda sendiri.

Anda tidak bisa memilih cherry dari kelas super .

Namun, mungkin ada situasi ketika Anda merasa perlu menelepon super.super.method()- biasanya tanda desain yang buruk, dalam kode Anda atau dalam kode yang Anda warisi!
Jika kelas super dan super super tidak dapat di-refactored (beberapa kode lawas), maka pilihlah komposisi daripada warisan.

Pemecahan enkapsulasi adalah ketika Anda @Override beberapa metode dengan memecah kode enkapsulasi. Metode yang dirancang untuk tidak diganti akan ditandai final .

DayaMoon
sumber
0

Dalam C # Anda dapat memanggil metode leluhur seperti ini:

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();
    }

Anda juga dapat melakukan ini di Delphi:

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

Tetapi di Jawa Anda dapat melakukan fokus seperti itu hanya dengan beberapa peralatan. Salah satu cara yang mungkin adalah:

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);
   }
} 

output hasil objC.DoIt ():

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
D.Motyl
sumber
Dalam C # itu hanya akan bekerja untuk metode non-virtual dan karena semua metode virtual di Jawa itu tidak benar-benar berbeda.
Agent_L
0

Ini mudah dilakukan. Contohnya:

C subclass dari B dan B subclass dari A. Keduanya memiliki metode methodName () misalnya.

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");
    }

}

Jalankan kelas C Output akan menjadi: Kelas A Kelas C

Alih-alih output: Kelas A Kelas B Kelas C

pengguna2490562
sumber
-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();
    }
}

Output: Dicetak di GrandDad

Andrey
sumber
2
Jawaban ini berada di luar ruang lingkup pertanyaan. OP tidak bertanya bagaimana memanggil metode dalam kelas kakek-nenek. Masalahnya adalah diskusi tentang mengapa super.super.method()kode tidak valid di Jawa.
Jed Schaaf
-1

Kata kunci super hanyalah cara untuk memanggil metode dalam superclass. Dalam tutorial Java: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Jika metode Anda menimpa salah satu metode superclass-nya, Anda dapat memanggil metode yang ditimpa melalui penggunaan kata kunci super.

Jangan percaya bahwa itu adalah referensi dari objek super !!! Tidak, itu hanya kata kunci untuk memanggil metode dalam superclass.

Berikut ini sebuah contoh:

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();
    }
}

Saat Anda menelepon cat.doSth(), metode doSth()di kelas Animalakan dicetak thisdan itu adalah kucing.

qing li
sumber