2016-05-21 4 views
1

Java 8Вызов виртуального метода от конструктора

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

public final class MyClass implements MyInterface { 
    private final Object[] arr; 
    public MyClass(){ 
     Object[] arr; 
     //init arr 
     this.arr = arr; 
     //Now we have to preprocess it 
     preprocess(); 
    } 

    @Override 
    public void preprocess(){ 
     //impl 
    } 

    public int count(){ 
     //impl 
    } 
} 

public interface MyInterface{ 
    void preprocess(); 
    int count(); 
} 

Есть ли другие подводные камни с вызовами виртуальных методов внутри конструктора? Конечно, я могу извлечь препроцесс в статический метод, а затем вызвать его из обоих, но он выглядит немного грязным. Я хотел бы сохранить код максимально чистым.

+0

Как последний класс представляет собой ловушку? Это просто означает, что вы не можете расширить этот класс ... Вам все еще нужно реализовать методы интерфейсов, поэтому вызов их в конструкторе не имеет ничего общего с окончательным классом –

+0

@ cricket_007 Нет, похоже, вы не понимаете. Дж. Блох в своей эффективной Java написал, что мы не должны вызывать неконкретный метод из конструктора. Если мы это сделаем, мы рискуем ввести ошибку. – user3663882

+0

@ cricket_007 спасибо, исправлено. – user3663882

ответ

4

Вы всегда должны проявлять осторожность при вызове методов от конструктора, поскольку конструкция объекта еще не завершена. Это справедливо даже для методов final и private, которые нельзя переопределить подклассами.

Пример:

public class Test { 
    public static void main(String[] args) { 
     new Sub().test(); 
    } 
} 
class Base { 
    int b; 
    Base() { 
     test(); 
     this.b = 1; 
    } 
    void test() { 
     System.out.println("Hello from Base. b = " + this.b); 
    } 
} 
class Sub extends Base { 
    int s; 
    Sub() { 
     test(); 
     this.s = 2; 
    } 
    @Override 
    void test() { 
     System.out.println("Hello from Sub. b = " + this.b + ", s = " + this.s); 
    } 
} 

ВЫХОД

Hello from Sub. b = 0, s = 0 
Hello from Sub. b = 1, s = 0 
Hello from Sub. b = 1, s = 2 

test() называется 3 раза: От Base конструктора, из Sub конструктора, а из main().

Как вы можете видеть, даже поле b еще не было инициализировано при первом вызове.

Итак, это незаконно? №
Вам следует избегать этого? Да.
Просто проясните (например, javadoc), что метод может быть вызван частично инициализированными объектами.

+0

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

+2

@ user3663882 Правильно. – Andreas

+0

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

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