Вопрос по java, polymorphism, access-modifiers, reflection – Могу ли я переопределить закрытый метод в Java?

12

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

public class SuperClass {

    public void printInt() {
        System.out.println("I am " + getClass() + ". The int is " + getInt());
    }

    private int getInt() {
        return 1;
    }
}

public class SubClass extends SuperClass {

    public static void main(String[] args) {
        (new SubClass()).printInt();
    }

    public int getInt() {
        return 2;
    }
}

Я хочуmain метод вSubClass  распечатать2, но это распечатывает1. I've heard this can be done through reflection, but I can't figure out how. If not reflection, does anyone know of another way of doing it? (Other than making SuperClass.getInt() защищены, или копирование и вставкаprintInt() метод вSubClass.) If actually overriding the private method is not possible, is there a way of placing some sort of trigger on it that will invoke a method in my sub-class either before or after the private method executes?

Он не виден дочернему классу, поэтому его нельзя переопределить. Bhavik Ambani
@ Xeon Даже javassist не может переопределить приватный метод, но ткачество во время загрузки может посоветовать приватные методы (не переопределяя их, хотя). Marko Topolnik
сделай этоprotected илиvisible ... иначе ты не сможешь Samir Mangroliya
"Могу ли я переопределить приватный метод в Java?" - ответ: нет Hovercraft Full Of Eels
placing some sort of trigger on it that will invoke a method in my sub-class either before or after the private method executes - вы можете использовать CGLIB или javassist с методами AOP, чтобы включить это.Spring использует так называемыйaround-advice тяжело. Xeon

Ваш Ответ

10   ответов
14

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

Однако вы можете получить доступ к приватному методуgetInt любого подкласса, вызывающегоprintInt вот так

public void printInt() throws Exception {
    Class<? extends SuperClass> clazz = getClass();
    System.out.println("I am " + clazz + ". The int is " +
                       clazz.getMethod("getInt").invoke(this) );
}

Это будет иметь эффект подкласса 'getInt метод, вызываемый из суперкласса 'printInt. Конечно, теперь это не удастся, если подкласс Не объявитьgetInt, поэтому вы должны добавить проверку, чтобы иметь возможность обрабатывать «нормальные» подклассы, которые не пытаются «переопределить» приватный метод:

public void printInt() throws Exception {
    Class<? extends SuperClass> clazz = getClass();

    // Use superclass method by default
    Method theGetInt = SuperClass.class.getDeclaredMethod("getInt");

    // Look for a subclass method
    Class<?> classWithGetInt = clazz;
    OUTER: while( classWithGetInt != SuperClass.class ){

        for( Method method : classWithGetInt.getDeclaredMethods() )
            if( method.getName().equals("getInt") && method.getParameterTypes().length == 0 ){
                theGetInt = method;
                break OUTER;
            }

        // Check superclass if not found
        classWithGetInt = classWithGetInt.getSuperclass();
    }

    System.out.println("I am " + classWithGetInt + ". The int is " + theGetInt.invoke(this) );
}

Вам все еще нужно изменить код суперкласса, чтобы это работало, и, поскольку вы должны изменить код суперкласса, вам просто нужно изменить модификатор доступа наgetInt вprotected вместо того, чтобы делать рефлексы.

Спасибо Trutheality. Этот подход похож на описанный выше @ michael-bosworth. Как вы говорите, мне все равно придется изменить код в своем суперклассе, и если он должен будет отразить каждый из своих частных методов перед его выполнением, то он будет работать как сла numbers longer
Я думаюfor условие цикла должно бытьfor( Method method : classWithGetInt.getDeclaredMethods() ). Ravi Thapliyal
17

потому что никакой другой класс, включая производный класс, не может сказать, что он существует. Эточастный

Частные методы неявноfinal.

В связанной заметке подкласс может объявить поле или метод с тем же именем, что иprivate поле или метод в суперклассе, потому что с точки зрения подкласса эти члены не существуют. Между этими членами нет особых отношений.

+ 1, я понятия не имел, что частные методы неявно являются окончательными ... имеет смысл, хотя! user1329572
Для вложенных классов любой код в тексте программы внешнего класса знает, что существует закрытый метод. Смотрите мой ответ для примера, которыймо работает - но не работает. Jon Skeet
@ JonSkeet Это правда, вроде. Закрытый для пакета метод синтезируется во внутреннем классе, который делегирует закрытый метод. Но с точки зрения программиста это выглядит так, как будто внешний класс может видеть закрытый метод внутреннего класса. erickson
@ erickson: Это не просто с точки зрения программиста, а с точки зрения Языка точка зрения. Я бы выделил его как JVM против языка. Jon Skeet
@ JonSkeet То есть, язык обеспечивает доступ от вложенных классов к закрытым членам вложенных, но JVM все еще действительно обеспечивает конфиденциальность? Я бы с этим согласился. erickson
13

ты не можешь. Вы можете создать программу, котораясмотрет как это должно быть в состоянии сделать это, используя тот факт, что код внутри внешнего класса может получить доступ к закрытым членам вложенного класса. Однако частные методы все еще не могут быть переопределены. Пример

public class Test {

    public static class Superclass {
        private void foo() {
            System.out.println("Superclass.foo");
        }
    }

    public static class Subclass extends Superclass {
        private void foo() {
            System.out.println("Subclass.foo");
            // Looks like it shouldn't work, but does...
            super.foo();
        }
    }

    public static void main(String[] args) throws Exception {
        Superclass x = new Subclass();
        // Only calls Superclass.foo...
        x.foo();
    }
}

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

Если вы хотите изменить поведение частного члена вашего суперкласса, ваш дизайн в основном нарушен.

5

но я нашел способ сделать это с помощью инструментов. Я использовал ASM, как предложено на Блог Друбы Бандопадхьяй. Если вам интересно, вы можете посмотреть на мой код (здесь слишком много информации).

Я не могу опубликовать более 2 ссылок, потому что я новый пользователь. Вы можете прочитать и скачать ASMВо. numbers longer
2

ты мог бы:

public class SuperClass {

    private final Method getInt;

    public SuperClass() {
        /** Find the appropriate method to call and cache it. **/
        Method getInt = null;
        try {
            getInt = getClass().getDeclaredMethod("getInt");
        }
        catch (NoSuchMethodException e) {
            try {
                getInt = SuperClass.class.getDeclaredMethod("getInt");
            } catch (NoSuchMethodException e1) {
                throw new RuntimeException(e1);
            }
        }
        getInt.setAccessible(true);
        this.getInt = getInt;
    }

    public void print() {
        int val = 0;
        try {
            val = (Integer) getInt.invoke(this);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        System.out.println(val);
    }

    private int getInt() {
        return 1;
    }
}

public class SubClass extends SuperClass {
    public int getInt() {
        return 2;
    }

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

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

Обратите внимание, что метод getInt () подкласса может быть закрытым, если вы используете отражение таким образом. Опять же, такой подход почти наверняка является плохой идеей, по крайней мере, по двум упомянутым причинам. Скорее всего, вы можете достичь своих целей другим способом. Защита пакетов не является опцией?

Спасибо Майкл. Интересное, но запутанное решение. Тем не менее, моя задача (заданная моим боссом) состоит в том, чтобы переопределить закрытый метод без изменения какого-либо кода в суперклассе. Так что это все еще не решает мою реальную проблему. (По крайней мере, я действительно надеюсь, что это не так - я не хочу тратить весь день на работу, написание такого кода!) numbers longer
0

классы с использованием Java?

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

(Обратите внимание, это только для целей спецификации)

VB:

function (ByRef intX as Integer) // значение может быть явно изменено, но не сам метод: P end function

да еще так, я думаю ...

0

мы не можем переопределить закрытые методы, но есть случай, когда мы можем переопределить и закрытые методы, то есть внутренний класс

class PrivateTest { 

private String msg = "OuterAndInner";

private void fun() {
     System.out.println("Outer fun()");
}

class Inner extends PrivateTest {
    private void fun()  {
          System.out.println("Accessing Private Member of Outer: " + msg);
    }
}
public static void main(String args[])  {
     PrivateTest o = new PrivateTest();
     Inner  i   = o.new Inner();
     i.fun();
     // o.fun() calls Outer's fun (No run-time polymorphism).
     o = i; 
     o.fun();
}

}

0

но вы можете ввести / переопределить его в производном класс

class Super
{
   private void fun()
   {
   }
}

class Child extends Super
{
    private void fun()
    {
    }
}

В этом случае вы не переопределяете функцию fun () в классе Child, вы определяете новый метод с тем же именем, но если вы попытаетесь использовать @override в верхней части fun () класса Child, вы получите ошибку времени компиляции. потому что вы вынуждены переопределить этот fun (), иначе вы просто переопределяете новый fun () в классе Child

0

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

«Переопределить» означает, что JVM может определять, какой метод использовать, основываясь на типе экземпляра, с которым вы его вызываете. Теория полиморфизма объясняет, что делает это возможным. Чтобы JVM могла сказать «эти методы связаны, один переопределяет другой», JVM должна иметь возможность просматривать Переопределяется метод из подкласса, который содержит Наиважнейшая метод.

При этом классы в предоставленном вами случае все равно будут компилироваться, но ни один из методов не будет вызываться на основе JVM, независимо запускающих вызов на основе экземпляра. Это будут просто разные методы, которые не будут признаны JVM переопределенными или переопределенными.

Более того, если вы используете IDE, например NetBeans, использование аннотации @Override сообщит компилятору, что вы переопределяете метод с той же сигнатурой в суперклассе. Если вы этого не сделаете, вы получите предупреждение, предлагающее сделать аннотацию. Однако вы получите ошибку компиляции, если примените эту аннотацию, когда:

В суперклассе нет метода с совпадающей сигнатурой

ИЛ

метод в суперклассе, который вы пытаетесь переопределить, помеченчастны или Финале

Обратитесь к официальной документации Oraclhttps: //docs.oracle.com/javase/tutorial/java/IandI/override.htm для следующей выдержки:

Методы экземпляра

... Возможность подкласса переопределять метод позволяет классу наследовать от суперкласса, поведение которого "достаточно близко", а затем изменять поведение по мере необходимости .... При переопределении метода вы можете захотеть использовать @Override аннотация, которая указывает компилятору, что вы намереваетесь переопределить метод в суперклассе. Если по какой-то причине компилятор обнаружит, что метод не существует ни в одном из суперклассов, он выдаст ошибку.

Overriding может быть очень запутанным, потому что JVM, по-видимому, действует независимо. Просто помните, что без доступа к методу [s] в суперклассе (другими словами, для всех методов, помеченныхчастны), подкласс не может вызвать или переопределить указанный метод [s], потому что он не может наследовать их для этого.

-3

PRIVATE методы НЕ Унаследованы ... Так как они не наследуются, они НЕ МОГУТ БЫТЬ ПЕРЕЗАГРУЗЕНЫ

Похожие вопросы