Любой способ _не_ вызвать конструктор суперкласса в Java? - PullRequest
24 голосов
/ 03 июня 2010

Если у меня есть класс:

class A {
   public A() { }
}

и еще

class B extends A {
   public B() { }
}

есть ли способ заставить B.B() не звонить A.A()?

Ответы [ 13 ]

28 голосов
/ 03 июня 2010

Нет абсолютно никакого способа сделать это на Java; это нарушит спецификацию языка.

Выполнение JLS 12 / 12.5 Создание экземпляров нового класса

Непосредственно перед возвращением ссылки на вновь созданный объект в качестве результата указанный конструктор обрабатывается для инициализации нового объекта, используя следующую процедуру:

  1. Назначьте аргументы для конструктора [...]
  2. Если этот конструктор начинается с явного вызова конструктора другого конструктора в том же классе (с использованием this), то [...]
  3. Этот конструктор не начинается с явного вызова конструктора другого конструктора в том же классе (используя this). Если этот конструктор предназначен для класса, отличного от Object, , тогда этот конструктор начнется с явного или неявного вызова конструктора суперкласса (с использованием super).
  4. Выполнить инициализаторы экземпляров и инициализаторы переменных экземпляров для этого класса [...]
  5. Выполнить остальную часть тела этого конструктора [...]
20 голосов
/ 03 июня 2010

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

public class A {
  protected Writer writer;

  public A() {
    init();
  }

  protected void init() {
    writer = new FileWriter(new File("foo.txt"));
  }
}

public class B extends A {
  protected void init() {
    writer = new PaperbackWriter();
  }
}

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

8 голосов
/ 03 июня 2010

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

1 голос
/ 09 января 2019

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

super(para_1, para_2,........);

class BaseA {
    BaseA(){
        System.out.println("This is BaseA");
    }
    BaseA(int a){
        System.out.println("This is BaseA a");
    }


}

class A extends BaseA {

    A(){
        super(5);
        System.out.println("This is A");
    }

    public static void main(String[] args) {
        A obj=new A();

    }
}


Output will be:
This is BaseA a
This is A
0 голосов
/ 30 августа 2017

Десериализация Java не вызывает конструктор, но кажется, что она основана на некоторых внутренних уловках JVM.

Однако, есть структура, которая позволяет вам делать это в переносимой форме: Objenesis (http://www.theserverside.com/discussions/thread/44297.html)

)

Я видел это недавно в Spring, когда при использовании прокси CGLIB Spring создает два экземпляра класса, но конструктор вызывается только один раз: https://stackoverflow.com/a/11583641/2557118

Это поведение добавлено в Spring 4:

Классы прокси на основе CGLIB больше не требуют конструктора по умолчанию. Поддержка предоставляется через библиотеку objenesis, которая перепакована встроенный и распространяемый как часть Spring Framework. С этим Стратегия, никакой конструктор вообще не вызывается для экземпляров прокси больше.

0 голосов
/ 09 января 2014

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

class SuperClass {
    protected SuperClass() {
        init();
    }

    // Added for classes (like ChildClassNew) who do not want the init to be invoked.
    protected SuperClass(boolean doInit) {
        if (doInit)
            init();
    }

    //
}

class ChildClass1 extends SuperClass {
    ChildClass1() {
        // This calls default constructor of super class even without calling super() explicitly.
        // ...
    }
    // ....
}

class ChildClass2 extends SuperClass {
    ChildClass2() {
        // This calls default constructor of super class even without calling super() explicitly.
        // ...
    }
    // ....
}

class ChildClassNew extends SuperClass {
    ChildClassNew() {
        /*
         * This is where I didn't want the super class' constructor to 
         * be invoked, because I didn't want the SuperClass' init() to be invoked.
         * So I added overloaded the SuperClass' constructor where it diesn;t call init().
         * And call the overloaded SuperClass' constructor from this new ChildClassNew.
         */
        super(false);
        // 
        // ...
    }
    // ....
}
0 голосов
/ 03 июня 2010
  1. Как указывает другой участник, B не расширяет A, поэтому он все равно не вызовет конструктор A.

  2. Нет способа сделать это на Java.

  3. Вероятно, вы можете выполнить то же самое, что и вы, следующим образом:

а) в каждый класс вашей иерархии включите конструктор с уникальной сигнатурой, который вызывает конструктор суперкласса со своими аргументами. Например, объявите класс «Noop» и конструктор, который принимает это в качестве аргумента:

public class NoOp {
}

public class class1 {
    class1() {
        System.out.println("class1() called");
    }
    class1(String x, String y) {
        System.out.println("class1(String, String) called");
}
    class1(NoOp x) {
        System.out.println("class1(NoOp) called");
    }
}

public class class2 extends class1 {
    class2() {
        System.out.println("class2() called");
    }
    class2(String x, String y) {
        System.out.println("class2(String, String) called");
}
    class2(NoOp x) {
        super(x);
        System.out.println("class2(NoOp) called");
    }
}

public class class3 extends class2 {
    class3() {
        System.out.println("class3() called");
    }
    class3(String x, String y) {
        super(new NoOp());
        System.out.println("class3(String, String) called");
    }
    class3(NoOp x) {
        super(x);
        System.out.println("class3(NoOp) called");
    }
    public static void main(String args[]) {
        class3 x = new class3("hello", "world");
    }
}

Если вы запустите это, вы получите вывод

class1(NoOp) called
class2(NoOp) called
class3(String, String) called

Итак, вы фактически создали конструктор класса 3, который вызывает только конструкторы, которые ничего не делают.

0 голосов
/ 03 июня 2010

Если вы имеете в виду

class B extends A {
     public B() { }
}

тогда уверен, что вы можете

class B extends A {
     public B() {
         this(abort());
     }
     private B(Void dummy) {
         /* super(); */
     }
     private static Void abort() {
         throw null;
     }
}

Не очень полезно. Интерфейс [не ключевое слово Java] к классу A говорит о том, что вам нужно запустить его конструктор, чтобы создать его, не без оснований Исключением является то, что сериализуемые классы создаются без вызова конструкторов сериализуемых классов.

0 голосов
/ 03 июня 2010

Каждый объект в Java является подклассом Object (объект с большой буквы «O»). когда вы создаете объект подкласса, вызывается конструктор суперкласса. Даже если ваш класс не наследует какой-либо другой класс, он неявным образом наследует Object, поэтому необходимо вызвать конструктор Object. Так что super () вызывается для этой цели.

0 голосов
/ 03 июня 2010

Нет - вы не можете это сделать, и почему вы все равно хотите это сделать? Это испортит вашу объектную модель.

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

Настоятельно советую не делать этого ...

...