Czy powinienem utworzyć instancję zmiennych instancji w deklaracji lub w konstruktorze?
Czy jest jakaś korzyść dla obu podejść?
Przykład 1:
class A {
B b = new B();
}
Przykład 2:
class A {
B b;
A() {
b = new B();
}
}
13 answers
- nie ma różnicy - inicjalizacja zmiennej instancji jest faktycznie umieszczana w konstruktorze (- ach) przez kompilator.
- pierwszy wariant jest bardziej czytelny.
- nie możesz mieć obsługi wyjątków z pierwszym wariantem.
-
Istnieje dodatkowo blok inicjalizacji, który jest również umieszczony w konstruktorze (- ach) przez kompilator:
{ a = new A(); }
Sprawdź wyjaśnienia i porady Sun
From this tutorial :
Deklaracje pól nie są jednak częścią żadnej metody, więc nie mogą być wykonywane tak, jak instrukcje. Zamiast tego kompilator Javy generuje kod inicjalizacji pola instancji automatycznie i umieszcza go w konstruktorze lub konstruktorach dla danej klasy. Kod inicjalizacji jest wstawiany do konstruktora w kolejności, w jakiej pojawia się w kodzie źródłowym, co oznacza, że inicjalizator pól może używać początkowych wartości pól zadeklarowanych przed to.
Dodatkowo możesz chcieć leniwie zainicjować swoje pole. W przypadku, gdy inicjalizacja pola jest kosztowną operacją, można zainicjować je tak szybko, jak jest to potrzebne:
ExpensiveObject o;
public ExpensiveObject getExpensiveObject() {
if (o == null) {
o = new ExpensiveObject();
}
return o;
}
I ostatecznie (jak zauważył Bill), ze względu na zarządzanie zależnościami, lepiej unikać używania operatora new
gdziekolwiek w twojej klasie. Zamiast tego lepiej jest użyć Dependency Injection - tzn. pozwolić komuś innemu (innemu class/framework) tworzy instancje i wprowadza zależności w swojej klasie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-09 14:54:27
Inną opcją byłoby użycie Dependency Injection.
class A{
B b;
A(B b) {
this.b = b;
}
}
Usuwa to odpowiedzialność za tworzenie B
obiektu z konstruktora A
. Dzięki temu Twój kod będzie bardziej testowalny i łatwiejszy do utrzymania w dłuższej perspektywie. Chodzi o zmniejszenie sprzężenia między dwiema klasami A
i B
. Zaletą, jaką daje Ci to jest to, że możesz teraz przekazać dowolny obiekt, który rozszerza B
(lub implementuje B
, jeśli jest to interfejs) do konstruktora A
i to zadziała. Jedną z wad jest rezygnacja z enkapsulacji obiektu B
, więc jest on narażony na wywołanie konstruktora A
. Będziesz musiał rozważyć, czy korzyści są warte tego kompromisu, ale w wielu przypadkach są.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-09 13:55:38
Poparzyłem się dzisiaj w ciekawy sposób:
class MyClass extends FooClass {
String a = null;
public MyClass() {
super(); // Superclass calls init();
}
@Override
protected void init() {
super.init();
if (something)
a = getStringYadaYada();
}
}
Widzisz błąd? Okazuje się, że inicjalizator a = null
zostanie wywołany po wywołaniu konstruktora klasy nadrzędnej. Ponieważ konstruktor klasy nadrzędnej wywołuje INIT (), inicjalizacją a
jest , a następnie inicjalizacja a = null
.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-20 00:08:57
Moja osobista "reguła" (prawie nigdy nie złamana) to:
- zadeklaruj wszystkie zmienne na początku blok
- uczynić wszystkie zmienne ostatecznymi, chyba że nie może być
- zadeklaruj jedną zmienną na linię
- nigdy nie inicjalizuj zmiennej, gdzie deklarowane
- tylko zainicjować coś w konstruktor gdy potrzebuje danych z konstruktor do wykonania inicjalizacja
Więc miałbym kod w stylu:
public class X
{
public static final int USED_AS_A_CASE_LABEL = 1; // only exception - the compiler makes me
private static final int A;
private final int b;
private int c;
static
{
A = 42;
}
{
b = 7;
}
public X(final int val)
{
c = val;
}
public void foo(final boolean f)
{
final int d;
final int e;
d = 7;
// I will eat my own eyes before using ?: - personal taste.
if(f)
{
e = 1;
}
else
{
e = 2;
}
}
}
W ten sposób zawsze mam 100% pewności, gdzie szukać deklaracje zmiennych (na początku bloku) i ich przypisania (jak tylko będzie to miało sens po deklaracji). To kończy się potencjalnie bardziej wydajne, ponieważ nigdy nie inicjalizujesz zmiennej z wartością, która nie jest używana (na przykład declare i init vars, a następnie rzucasz wyjątek przed połową tych vars potrzebnych do uzyskania wartości). Nie kończy się to bezsensowną inicjalizacją (jak int i = 0; a później, przed użyciem "i", czy i = 5;.
I bardzo cenię spójność, więc przestrzeganie tej "zasady" jest czymś, co robię cały czas, a to znacznie ułatwia pracę z kodem, ponieważ nie musisz polować, aby znaleźć rzeczy.
Twój przebieg może się różnić.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-23 18:34:21
Przykład 2 jest mniej elastyczny. Jeśli dodasz inny konstruktor, musisz pamiętać, aby utworzyć instancję pola w tym konstruktorze. Wystarczy utworzyć instancję pola bezpośrednio lub wprowadzić leniwe Ładowanie gdzieś w getterze.
Jeśli instancja wymaga czegoś więcej niż tylko prostego new
, Użyj bloku inicjującego. Będzie to uruchamiane niezależnie od używanego konstruktora. Np.
public class A {
private Properties properties;
{
try {
properties = new Properties();
properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("file.properties"));
} catch (IOException e) {
throw new ConfigurationException("Failed to load properties file.", e); // It's a subclass of RuntimeException.
}
}
// ...
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-18 15:03:26
Rozumiem, że to prawie tylko kwestia gustu, o ile inicjalizacja jest prosta i nie wymaga żadnej logiki.
Podejście konstruktora jest nieco bardziej kruche, jeśli nie używasz bloku inicjującego, ponieważ jeśli później dodasz drugi konstruktor i zapomnisz zainicjalizować b, otrzymasz null b tylko wtedy, gdy użyjesz tego ostatniego konstruktora.
Zobacz http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html aby uzyskać więcej informacji na temat inicjalizacji w Javie (i objaśnienia dotyczące bloków initalizera i innych mało znanych funkcji inicjalizacji).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 07:31:47
Używanie dependency injection lub leniwa inicjalizacja jest zawsze preferowane, co zostało już dokładnie wyjaśnione w innych odpowiedziach.
Jeśli nie chcesz lub nie możesz używać tych wzorców, a dla prymitywnych typów danych, są trzy ważne powody, dla których mogę myśleć, dlaczego lepiej jest zainicjować atrybuty klasy poza konstruktorem:
- unikanie powtórzeń = Jeśli masz więcej niż jeden konstruktor, lub kiedy będziesz musiał dodaj więcej, nie będziesz musiał powtarzać inicjalizacji w kółko we wszystkich ciałach konstruktorów;
- poprawiona czytelność = możesz łatwo określić, które zmienne będą musiały zostać zainicjowane spoza klasy;
- zredukowane linie kodu = dla każdej inicjalizacji wykonanej w deklaracji będzie mniej linii w konstruktorze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-27 18:33:22
Obie metody są dopuszczalne. Zauważ, że w tym drugim przypadku b=new B()
może nie zostać zainicjalizowany, jeśli obecny jest inny konstruktor. Pomyśl o kodzie inicjalizacyjnym poza konstruktorem jako o wspólnym konstruktorze i Kod zostanie wykonany.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 07:00:38
Myślę, że Przykład 2 jest lepszy. Myślę, że najlepszą praktyką jest zadeklarowanie poza konstruktorem i zainicjowanie w konstruktorze.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 07:01:26
Drugi jest przykładem leniwej inicjalizacji. Pierwszym z nich jest prostsza inicjalizacja, są one zasadniczo takie same.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 07:01:51
Jest jeszcze jeden subtelny powód inicjalizacji poza konstruktorem, o którym nikt wcześniej nie wspomniał(bardzo konkretny muszę powiedzieć). Jeśli używasz narzędzi UML do generowania diagramów klas z kodu (inżynieria odwrotna), większość narzędzi, jak sądzę, zwróci uwagę na inicjalizację przykładu 1 i przeniesie go na diagram(jeśli wolisz, aby pokazywał wartości początkowe, tak jak ja). Nie będą brać tych wartości początkowych z przykładu 2. Ponownie, jest to bardzo konkretny powód - jeśli pracuję z narzędziami UML, ale kiedy się o tym dowiedziałem, staram się wyjąć wszystkie moje domyślne wartości poza konstruktor, chyba że, jak wspomniano wcześniej, jest problem ewentualnego rzucania wyjątków lub skomplikowanej logiki.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-09 14:15:52
Druga opcja jest preferowana, ponieważ pozwala na użycie innej logiki w ctors do tworzenia instancji klasy i używania łańcucha ctors. Np.
class A {
int b;
// secondary ctor
A(String b) {
this(Integer.valueOf(b));
}
// primary ctor
A(int b) {
this.b = b;
}
}
Więc druga opcja jest bardziej elastyczna.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-14 14:10:28
W odpowiedziach nie widziałem:
Ewentualną zaletą inicjalizacji w momencie deklaracji może być obecnie IDE, gdzie można bardzo łatwo przejść do deklaracji zmiennej (głównie
Ctrl-<hover_over_the_variable>-<left_mouse_click>
) z dowolnego miejsca w Twoim kodzie. Następnie natychmiast widzisz wartość tej zmiennej. W przeciwnym razie trzeba "wyszukać" miejsce, w którym odbywa się inicjalizacja (najczęściej: constructor).
Ta przewaga jest oczywiście drugorzędna w stosunku do wszystkich innych logiczne rozumowania, ale dla niektórych "cecha" może być ważniejsza.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-30 09:13:27