2009-02-25 2 views
299

Я прочитал this question и подумал, что было бы легко решить (не то, что она не разрешима без), если можно было бы написать:Почему super.super.method(); не разрешено в Java?

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

Я не уверен, если это полезно во многих случаях, но интересно почему это не так, и если что-то подобное существует на других языках.

Что вы, ребята, думаете?

EDIT: Чтобы уточнить: да, я знаю, это невозможно на Java, и я действительно не скучаю по нему. Это ничего, что я ожидал от работы, и был удивлен получением ошибки компилятора. У меня была идея и я хотел бы обсудить это.

+5

Хотеть называть 'super.super.toString()' противоречит собственному решению, когда вы решите расширить класс таким образом принимая ** все ** (не некоторые) его особенности. – DayaMoon

ответ

423

Он нарушает инкапсуляцию. Вы не сможете обойти поведение родительского класса. Иногда имеет смысл обходить ваш класс (особенно из-за того же метода), но не у вашего родителя. Например, предположим, что у нас есть базовый «набор элементов», подкласс, представляющий «коллекцию красных элементов» и подкласс того, что представляет собой «коллекцию больших красных элементов». Это имеет смысл иметь:

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

Это нормально - RedItems всегда может быть уверен в том, что элементы в нем содержится все красные. Теперь предположим, что мы были способны вызвать 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); 
    } 
} 

Теперь мы могли бы добавить, что мы хотели, и инвариант в RedItems нарушается.

Это имеет смысл?

+31

хороший пример. но я думал, что это плохой дизайн, когда базовый класс принимает элементы, но производный класс отвергает их, потому что производный не может использоваться в качестве замены для базового класса (нарушение принципа подстановки). это правильное мышление или такая иерархия чиста? –

+0

Спасибо за ваш ответ, это хороший момент. Но это также причина, по которой вам иногда следует выбирать инкапсуляцию над наследованием, не так ли? –

+4

Вы имеете в виду композицию над наследством? Этот пример не является причиной, чтобы избежать наследования - хотя есть много других. –

3

Я думаю, что если вы перепишете метод и хотите все его суперклассовую версию (например, для equals), то вы практически всегда хотите сначала назвать версию прямого суперкласса, которая будет называться ее суперклассовой версией в свою очередь, если захочет.

Я думаю, что это редко вызывает чувство (если вообще я не могу придумать случай, когда он это делает), чтобы вызвать какую-то произвольную версию суперкласса метода. Я не знаю, возможно ли это вообще на Java. Это можно сделать в C++:

this->ReallyTheBase::foo(); 
+3

Имеет смысл, если кто-то написал подкласс с одним методом, реализованным неправильно, но метод суперкласса выполняет 90% работы. Затем вы хотите создать подкласс и переопределить метод, вызывающий метод суперкласса суперкласса, и добавить свои собственные 10%. –

2

Угадайте, потому что он часто не используется. Единственная причина, по которой я мог его использовать, - это если ваш прямой родитель переопределил некоторые функции, и вы пытаетесь восстановить его обратно к оригиналу.

Кажется, что я против принципов OO, поскольку прямой родительский класс должен быть более тесно связан с вашим классом, чем бабушка и дедушка.

7

В дополнение к очень хорошим моментам, которые сделали другие, я думаю, есть еще одна причина: что, если у суперкласса нет суперкласса?

Поскольку каждый класс, естественно, простирается (как минимум) Object, super.whatever() всегда будет ссылаться на метод в суперклассе. Но что, если ваш класс только расширяет Object - что бы super.super ссылается на то? Как следует обрабатывать это поведение - ошибка компилятора, NullPointer и т. Д.?

Я думаю, что основная причина, по которой это недопустимо, заключается в том, что она нарушает инкапсуляцию, но это может быть и небольшая причина.

+2

Очевидно, что это была бы ошибка компилятора - и это информация, которую имеет компилятор. –

10

У меня недостаточно репутации для комментариев, поэтому я добавлю это к другим ответам.

Jon Skeet отвечает превосходно, с красивым примером. У Мэтта Б есть точка: не все суперклассы имеют супер. Ваш код сломался бы, если бы вы назвали супер супер, у которого не было супер.

Объектно-ориентированное программирование (которое есть Java) - это все объекты, а не функции. Если вы хотите ориентированное на задачи программирование, выберите C++ или что-то еще. Если ваш объект не подходит в суперкласс, вам нужно добавить его в класс «grandparent class», создать новый класс или найти другой супер, в который он вписывается.

Лично я нашел это ограничение одним из величайших преимуществ Java. Код несколько жесткий по сравнению с другими языками, которые я использовал, но я всегда знаю, чего ожидать. Это помогает с «простой и знакомой» целью Java. На мой взгляд, вызов super.super не прост или не знаком. Возможно, разработчики чувствовали то же самое?

+3

Вы говорите: «у не всех суперклассов есть суперсы». Ну, все, кроме java. lang.Object, что может дать вам «null». Поэтому я бы сказал, что почти у всех есть суперсы. –

+0

Каждый класс, который пишет программатор программиста, имеет «супер» (java.lang.Object нет, но программа-программист не напишите это.) – finnw

+1

Легко решить, сделав super.super.super ... супер ошибку времени компиляции, если слишком много суперсовместимости. Учитывая, что Java имеет только публичное наследование, если кто-то изменил иерархии наследования, вы меняете интерфейс. Поэтому я не стал бы волноваться, что супер^страшно. –

57

Я думаю, что у Джона Скита есть правильный ответ. Я просто хотел бы добавить, что вы можете доступ слежки переменных из суперклассов суперкласса литья 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(); 
     } 
} 

, который выводит:

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

(пример из JLS)

Однако этот doesn't work for method calls, поскольку вызовы методов определяются на основе типа среды выполнения объекта.

+0

Это может сработать, но для чего это полезно? – finnw

+6

Если у вас есть такое же имя, как у суперкласса, и по какой-то причине вы не можете (или не можете) изменить его, вы все равно можете получить доступ к переменной суперкласса с тем же именем. Что вам нужно, спросите вы? Ну ... я никогда не использовал его. –

+0

Отличная ссылка на документ выражений. Некоторые приятные примеры для людей, изучающих OCPJP. – Gordon

1

Возможно, по крайней мере, получить класс суперкласса суперкласса, хотя и не обязательно его экземпляр, используя отражение; если это может быть полезно, пожалуйста, рассмотрите Javadoc по адресу http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getSuperclass()

37

Я думаю, что следующий код позволяет использовать super.super ... super.method() в большинстве случаев. (даже если это uggly сделать)

Короче

  1. создать временный экземпляр типа предка
  2. копирования значения полей из оригинального объекта временного один
  3. Invoke целевого метода на временный объект
  4. копировать модифицированные значения обратно в исходный объект

Использование:

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

С отражением вы можете делать все, да :) Вы даже можете сделать строки изменчивыми. – BalusC

+17

Какая ужасная вещь! Я даю вам +1 только для того, чтобы понять, как это сделать :) –

+6

Это хороший трюк, но даже это не всегда эквивалентно вызову непригодного, но необходимо) super.super, и это потому, что super.super вызов будет нести контекст C (C + B + A), тогда как ваши ответы создают экземпляр A без контекста B и C. Таким образом, этот ответ не будет работать, если каждый doThat, называемый getContext(), например, и getContext был реализованы по-разному в каждом классе. в вашем ответе он использовал бы getContext(), тогда как вызов недоступного super.super привел бы к использованию getContext. – inor

7

Там несколько хороших причин, чтобы сделать это. У вас может быть подкласс, у которого есть метод, который реализован неправильно, но родительский метод реализован правильно. Поскольку он принадлежит сторонней библиотеке, вы не можете/не хотите изменять источник. В этом случае вы хотите создать подкласс, но переопределите один метод для вызова метода super.super.

Как показали некоторые другие плакаты, можно сделать это с помощью отражения, но это должно быть возможно сделать что-то вроде

(SuperSuperClass это) .theMethod();

Я имею дело с этой проблемой прямо сейчас - быстро исправить это скопировать и вставить метод суперкласса в метод subsubclass :)

+0

Кастинг перед вызовом метода не изменяет метод, который делегируется - это всегда реализация подкласса, которая используется, а не супер. См. Например, http://stackoverflow.com/questions/1677993/java-inheritance-resolution-in-case-of-instance-methods-and-variables/1678023#1678023 –

+1

@ Larry это именно та ситуация, в которой я был , и именно то, что я использовал. Хороший звонок! – bcr

+0

Если у вас есть код необходимого метода, вы можете открыть все необходимые поля и запустить этот код в своем подклассе. – Enyby

0

IMO, это чистый способ достижения super.super.sayYourName() поведения в Java.

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

Выход:

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

+0

ах, так вы выступаете за реализацию иерархии классов, как это? К сожалению, это начинает становиться действительно грязным, если вы хотите получить доступ к методу в маме из Baby (дочерний подкласс) ... – bcr

+0

yakov fain прав. Другими словами, это не очень хороший пример, потому что исходный вопрос заключался в вызове переопределенного метода в super.super. – inor

+0

.... и в вашем примере GrandMa.sayYourName не переопределяется – inor

0

Если вы думаете, что вы собираетесь нуждаться в суперкласс, вы можете ссылаться на него в переменной для этого класса. Например:

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

Если распечатать:

2 
1 
0 
+2

Ваш exmaple вроде ... плохой, потому что вы используете статические методы. При использовании статических методов вам не нужна переменная или супер. Возможно, вы пропустили какую-то основную концепцию OO здесь, убедитесь, что вы это прочитали. Приходится смириться, извините. –

+1

Это можно было легко сделать без статических методов. Это достаточно просто. Я собирался на простой пример того, как это сделать. – Ashtheking

+0

Я получаю вашу точку, сохраняя супер переменную в поле - это один из способов решить эту проблему. Но вам не нужна переменная, если это статический метод, вы можете просто использовать его. Во-вторых, при вызове статических методов одна из переменных является плохой практикой, и большинство IDE предупредит вас об этом. Если вы исправите свой ответ, удалив статические вещи, я буду рад удалить мой нисходящий поток, без обид. –

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

    } 
} 

пробег: BUILD УСПЕШНОГО (общее время: 0 секунд)

+1

Я вижу, но вы создаете новый экземпляр, чтобы это не сработало, если объект имеет какое-либо состояние. –

0

Призвание super.super.method () имеют смысл, когда вы не можете изменить код базового класса. Это часто происходит, когда вы расширяете существующую библиотеку.

Спросите себя, почему вы расширяете этот класс? Если ответ «потому что я не могу изменить его», то вы можете создать точный пакет и класс в вашем приложении, и переписать озорной метод или создать делегат:

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

Например, вы можете создать org.springframework. test.context.junit4.SpringJUnit4ClassRunner в вашем приложении, поэтому этот класс должен быть загружен до реального из jar. Затем перепишите методы или конструкторы.

Внимание: Это абсолютный взлом, и его НЕ рекомендуется использовать, но это РАБОТА! Использование этого подхода опасно из-за возможных проблем с загрузчиками классов. Также это может вызвать проблемы при каждом обновлении библиотеки, содержащей перезаписанный класс.

1

У меня были ситуации, подобные этим, когда архитектура должна создавать общие функции в общем CustomBaseClass, который реализуется от имени нескольких производных классов. Однако нам нужно обойти общую логику для конкретного метода для определенного производного класса. В таких случаях мы должны использовать реализацию super.super.methodX.

Мы достигаем этого, введя логический элемент в CustomBaseClass, который может использоваться для выборочного отсрочки пользовательской реализации и приведения к реализации реализации по умолчанию по умолчанию, когда это желательно.

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

Однако, с хорошими принципами архитектуры следовали в рамках, а также приложения, мы могли бы избежать таких ситуаций легко, используя HASA подход, вместо Исы подхода.Но во все времена не очень практично ожидать хорошо продуманной архитектуры, и, следовательно, необходимо избегать твердых принципов проектирования и внедрять такие хаки. Только мои 2 цента ...

1

@Jon Skeet Nice описание. IMO, если кто-то хочет вызвать метод super.super, тогда нужно игнорировать поведение непосредственного родителя, но хотите получить доступ к поведению великого родителя. Это может быть достигнуто с помощью экземпляра. Как ниже кода

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

Вот класс драйвера,

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

Выход этого будет

In C Class 
In A Class 

поведение класса B printClass будут проигнорированы в этом случае. Я не уверен, что это идеальная или хорошая практика для достижения super.super, но все же она работает.

+1

Ну, это креативно, но на самом деле не отвечает на мой вопрос. C по-прежнему не вызывает super.super, B просто ведет себя по-другому. Если вы можете изменить A и B, вы можете просто добавить другой метод вместо использования instanceof. Super.super.foo поможет вам в тех случаях, когда у вас нет доступа к A и B и вы не можете их изменить. –

+0

Согласен @TimButhe, но если кто-то хочет вызвать super.super, то он/она намеренно хочет игнорировать поведение родительского класса, поэтому вам просто нужно достичь этой вещи существующим синтаксисом java. (Любой вариант, который вы хотите использовать как instanceof/different method) –

2

Я бы поставил тело метода super.super в другом методе, если это возможно

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

Или, если вы не можете изменить супер-супер класс, вы можете попробовать это:

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

В в обоих случаях, результаты

new ChildClass().toString(); 

к "Я супер супер"

+0

Я просто оказался в ситуации, когда у меня есть SuperSuperClass и ChildClass, но не SuperClass, поэтому я нашел первое решение полезным. – xofon

0

Я думаю, что это проблема, которая нарушает соглашение о наследовании.
Расширяя класс, который вы слушаете/соглашаетесь с его поведением, функции
В то же время, когда вы звоните super.super.method(), вы хотите разорвать свое собственное соглашение о послушании.

Вы просто не можете выбрать вишню из суперкласса.

Однако могут возникнуть ситуации, когда вы чувствуете необходимость звонить super.super.method() - обычно плохой знак дизайна, в вашем коде или в коде, который вы наследуете!
Если супер и супер супер классы нельзя реорганизовать (некоторый унаследованный код), а затем выбрать композицию над наследованием.

Инкапсуляция ломается, когда вы @Override некоторые методы, разбирая инкапсулированный код. Методы, предназначенные для переопределения, отмечены final.

-1

Это просто сделать. Например:

Подкласс подкласса B и B подкласса A. Оба из них имеют метод methodName(), например.

общественного абстрактного класса А {

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

}

общественного класса B расширяет A {

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

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

}

общественного класса C расширяет 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"); 
} 

}

Выполнить класс C Выход будет: Класс A Класс C

Вместо того, чтобы выход: Класс A Класс B Класс 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(); 
    } 
} 

Выход: напечатанных в GrandDad

+2

Этот ответ выходит за рамки вопроса. ОП не спрашивал, как вызвать метод в классе grandparent. Проблема заключается в обсуждении того, почему 'super.super.method() 'недопустимый код в Java. –

0

В C# вы можете вызвать метод любого предка, подобного этому :

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

Также вы можете сделать это в Delphi:

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

Но в Java вы можете сделать такой фокус только на какой-то механизм. Одним из возможных способов являются:

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() вывод результата:

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 
+0

В C# он будет работать только для не виртуальных методов, и поскольку все методы виртуальны в Java, это не совсем так. –

Смежные вопросы