2010-08-02 5 views
15

У меня возникли проблемы с использованием нескольких конструкторов в java.Перегруженный конструктор вызывает другой конструктор, но не как первый оператор

то, что я хочу сделать что-то вроде этого:

public class MyClass { 

// first constructor 
public MyClass(arg1, arg2, arg3) { 
    // do some construction 
} 

// second constructor 
public MyClass(arg1) { 
     // do some stuff to calculate arg2 and arg3 
     this(arg1, arg2, arg3); 
    } 
} 

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

Какое общее решение для такой ситуации? Я не могу вычислить arg2 и arg3 «в строке». Я подумал, может быть, создать метод вспомогательного строительства, который будет выполнять фактическую конструкцию, но я не уверен, что это так «хорошо» ...

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

ответ

21

Как правило, используйте другой распространенный метод - «вспомогательный элемент конструкции», как вы предложили.

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
     init(arg1, arg2, arg3); 
    } 

    // second constructor 
    public MyClass(int arg1) { 
     // do some stuff to calculate arg2 and arg3 
     init(arg1, arg2, arg3); 
    } 

    private init(int arg1, int arg2, int arg3) { 
     // do some construction 
    } 
} 

Альтернативы является подходом заводского стиля, в котором у вас есть MyClassFactory, который дает вам MyClass экземпляров, и MyClass имеет только один конструктор:

public class MyClass { 

    // constructor 
    public MyClass(arg1, arg2, arg3) { 
     // do some construction 
    } 
} 

public class MyClassFactory { 

    public static MyClass MakeMyClass(arg1, arg2, arg3) { 
     return new MyClass(arg1, arg2, arg3); 
    } 

    public static MyClass MakeMyClass(arg1) { 
     // do some stuff to calculate arg2 and arg3 
     return new MyClass(arg1, arg2, arg3); 
    } 
} 

Я определенно предпочитаю первый вариант.

+1

Иногда может быть полезно иметь возможность выбросить исключение, и это плохая привычка бросать его из конструкторов. Для этих ситуаций метод фабрики лучше – Gaim

+3

Я определенно предпочитаю второй вариант, если вы поместите заводский метод в сам класс, а не в другой класс. Первый параметр не позволяет вам назначать поля с параметрами «final». Компилятор не может сказать, что метод 'init' вызывается один раз в каждом конструкторе и никогда не находится за пределами конструктора. Если вам не нравится метод фабрики, см. Мой ответ для другого параметра при вычислении других параметров. – Jorn

+1

@Jorn Я со Стивом Егге на фабриках, они всегда раздражали меня. –

9

Следующее возможное решение: Factory method. Эти статические методы могут быть перегружены и после расчета они могут вызвать частный/защищенный конструктор

public class MyClass { 

    private MyClass(arg1, arg2, arg3) { 
     // do sth 
    } 

    public static MyClass getInstance(arg1) { 
     // calculate arg2,3 
     return new MyClass(arg1, arg2, arg3); 
    } 

    public static MyClass getInstance(arg1, arg2, arg3) { 
     return new MyClass(arg1, arg2, arg3); 
    } 
} 

EDIT: Этот метод также идеально подходит, если у вас есть конечные поля

0

Вы можете переместить код MyClass(arg1, arg2, arg3) в вспомогательный метод (назовите его Init или что-то еще), а затем вызовите этот метод в обоих конструкторах.

+0

Мне не нравится эта опция, потому что она не позволяет вам назначать поля с конечными значениями параметров. Компилятор не может сказать, что метод 'init' вызывается один раз в каждом конструкторе и никогда не находится за пределами конструктора. – Jorn

0

Вы можете создать factory method, который вызывает конструктор:

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
    // do some construction 

    } 

    // second constructor as factory method 
    public static createMyClassAndDoFunkyStuff(int arg1) { 
     // do some stuff to calculate arg2 and arg3 
     return new MyClass(arg1, arg2, arg3); 
    } 

} 
+0

вместо 'return new this (...)' it должно быть 'return new MyClass (...)' – Jorn

+2

Это решение действительно беспорядочно, потому что иногда вы используете прямой вызов (конструктор), а иногда и косвенный (заводский метод) – Gaim

+1

I Согласитесь: в этом случае вы также должны создать фабричный метод, который принимает все три аргумента, и вместо этого создайте конструктор 'public'' private'. – Jorn

4

хелперные и фабричные варианты очень хороши.

Существует еще один:

public MyClass(int arg1) { 
    this(arg1, calculateArg2(), calculateArg3()); 
} 

private static int calculateArg2() {..} 
private static int calculateArg3() {..} 
+0

Довольно приятно, что там можно использовать статические методы. –

+0

yay для необъяснимого понижения. Возможно, вы ответили бы на вопрос Юрна, который по сути тот же? Или вы - Jorn;) – Bozho

+2

Это не я, но спасибо за вотум доверия. – Jorn

9

Хотя я предпочитаю вариант фабричный метод указывает несколько других ответов, я хотел бы предложить другой вариант: Вы можете использовать статические методы, чтобы сделать расчет ваших других параметров :

public class MyClass { 
    public MyClass(int arg1, int arg2, int arg3) { 
     // do some construction 
    } 

    public MyClass(int arg1) { 
     //call to this() must be the first one 
     this(arg1, calculateArg2(arg1), calculateArg3()); 
     //you can do other stuff here 
    } 

    private static int calculateArg2(int arg1) { 
     //calc arg2 here 
    } 

    private static int calculateArg3() { 
     //calc arg3 here 
    } 
} 
+0

+1 хорошая идея Jorn! –

0

Другой способ заключается в следующем:

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
    // do some construction 
    doSomeStuffToArg3Arg3(arg2, arg3) 
    } 

    // second constructor 
    public MyClass(int arg1) { 
     this(arg1, arg2, arg3); 
    } 

    private void doSomeStuffToArg3Arg3(int arg2, int arg3) { 
    // do some stuff to calculate arg2 and arg3 
    } 
} 
0

Как альтернативой указанным ответам, самый простой способ - реорганизовать вычисление аргумента на конструктор 3 аргументов;

public class MyClass { 

    // first constructor 
    public MyClass(arg1, arg2, arg3) { 
     if (null == arg2) { 
      // calculate arg2 
     } 
     if (null == arg3) { 
      // calculate arg3 
     } 
     // do some construction 
    } 

    // second constructor 
    public MyClass(arg1) { 
     this(arg1, null, null); 
    } 
} 
3

Использовать значения маркеров для 'отсутствует'

public class MyClass { 
public MyClass(arg1, arg2, arg3) { 
    // do some stuff to calculate arg2 and arg3 if they are the missing values 
    // do some construction 
} 
public MyClass(arg1) { 
    this(arg1, null, null); 
} 
} 

Для достижения наилучших результатов, сделать 'общий' конструктор protected или private.

+0

+ приятно! Мне нравится вся творческая идея, которая появляется здесь. –

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