Могу ли я переопределить частный метод в Java?

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

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. Я слышал, что это можно сделать с помощью размышлений, но я не могу понять, как это сделать. Если не отражение, знает ли кто-нибудь другой способ сделать это? (За исключением защиты SuperClass.getInt(), или копирования и вставки метода printInt() в SubClass.) Если фактическое переопределение частного метода невозможно, существует ли способ размещения на нем какого-то триггера, который будет вызывать метод в моем подклассе до или после выполнения частного метода?

Ответ 1

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

Однако вы можете получить доступ к частному методу 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 вместо того, чтобы делать обходные ситуации.

Ответ 2

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

Ответ 5

Если вы действительно хотели сделать это с помощью отражения, вы могли бы:

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() может быть закрытым, если вы используете отражение таким образом. Опять же, этот подход почти определенно является плохим идеей, по крайней мере, по двум упомянутым причинам. Скорее всего, вы можете достичь своих целей другим способом. Защита пакетов на самом деле не вариант?

Ответ 6

Вы можете передать var по ссылке → классы с помощью java?

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

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

VB:

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

настолько больше, что я думаю...

Ответ 7

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

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 fun (No run-time polymorphism).
     o = i; 
     o.fun();
}

}

Ответ 8

Вы не можете переопределить частный метод, но вы можете ввести/переопределить его в производном классе. Подобно:

class Super
{
   private void fun()
   {
   }
}

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

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

Ответ 9

Вы не можете переопределить частный метод.

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

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

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

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

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

ИЛИ

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

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

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

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

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

Ответ 10

Прочтите это внимательно,

ЧАСТНЫЕ методы НЕ ВХОДЯТСЯ... Поскольку они не унаследованы, они НЕ МОГУТ БЫТЬ ПЕРЕЗАГРУЗЛЕНЫ