Почему в java есть требование, чтобы первая строка конструктора вызывала родительский конструктор?Есть ли подводные камни, если мы обойдем это требование? - PullRequest
3 голосов
/ 01 июля 2010

У меня есть следующий код:

class Foo {

   public Foo (String param) { ... }
}

class Bar extends Foo {

   public Bar () {
      super (doSmth());
      ...
   }

   private static String doSmth () {
      //what I can NOT do here?
   }
}

Интересно, это безопасно?Существуют ли какие-либо ограничения относительно метода doSmth?

Ответы [ 5 ]

5 голосов
/ 01 июля 2010

Простое правило - не обращаться прямо или косвенно к объекту "this" из конструктора.

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

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

В вашем конкретном случае то, что у вас есть, хорошо. Если это изменится на:

class Bar extends Foo 
{
    public Bar () {
        super (doSmth(this));
        ...
    }

    private static String doSmth (Bar bar) {
        //what I can NOT do here?
   }
}

Тогда у вас будет (потенциальная) проблема, потому что doSmth может вызвать переопределенный метод в подклассе Bar, который использует данные, которые еще не были инициализированы.

Вот пример того, что может произойти:

public class Main
{
    public static void main(final String[] argv)
    {
        final A a;

        a = new B();
        a.foo();
    }
}

abstract class A
{
    protected A()
    {
        bar(this);
    }

    private static void bar(final A a)
    {
        a.foo();
    }

    public abstract void foo();
}

class B extends A
{
    final String str;

    public B()
    {
        super();
        str = "Hello, World!";
    }

    public void foo()
    {
        System.out.println("str - " + str);
    }
}

Итак, пока вы не вызываете переопределенные методы, вы хороши. Однако безопаснее всего просто следовать «правилу» - никогда не передавать это за пределы конструктора и никогда не вызывать переопределяемые (не финальные) методы, прямо или косвенно из конструктора.

3 голосов
/ 01 июля 2010

Я пока не вижу, чтобы кто-нибудь отвечал на ваш вопрос ...

private static String doSmth () {
    //what I can NOT do here?
}

Ты можешь делать там все, что захочешь. Метод является статическим, поэтому вы можете получить доступ ко всем полям static , которые вам нужны. Ваш компилятор остановит вас от попыток доступа к специфическим для экземпляра методам / полям.

Кроме того, ничто не мешает вам использовать статический метод в конструкторе или даже в качестве предпосылки для вызова конструктора super():

public Bar () {
    super (doSmth());
    /* ... */
 }

... пока doSmth() является статичным , вы золотой.

3 голосов
/ 01 июля 2010

Обход требования очень небезопасен, поскольку он позволит вам вызывать метод базового класса до запуска его конструктора. Там могут случиться только плохие вещи. Вот почему Java (и большинство других вменяемых языков заставляют это делать)

class Foo { 
  string m_field;
  public Foo(string param) {
    m_field = param;
  }
  public void Method() {
    // use m_field
  }
}

class Bar extends Foo { 
  public Bar() {
    // no super
    Method();  // oops
  }
}
1 голос
/ 01 июля 2010

Иногда я использую его для создания параметров для конструктора суперкласса.Но метод должен быть статическим:

class MyImplementation extends SuperImplementation {
   public MyImplementation(String input) {
      super(convertInput(input));
      ...
   }

   private static String convertInput (String input) {
      String output = "";
      // do something with input and put it to output
      return output;
   }
 }
0 голосов
/ 05 февраля 2013

На вопрос, почему в java есть требование, чтобы первая строка конструктора вызывала родительский конструктор?

Я полагаю, что это потому, что java учитывает это при принятии решения о внедрении конструктора по умолчанию

class Foo(){
abc()
this()

}

может стать

class Foo(){
this()
abc()
this()

}

Отсюда http://www.leepoint.net/notes-java/oop/constructors/constructor.html

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...