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();
    }
}
Author: Jeroen Vannevel, 2010-01-03

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.

 246
Author: Bozho,
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ą.

 34
Author: Bill the Lizard,
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.
 14
Author: Edward Falk,
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ć.
 14
Author: TofuBeer,
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.
        }
    }

    // ...

}
 7
Author: BalusC,
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).

 4
Author: Vinko Vrsalovic,
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:

  1. 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;
  2. poprawiona czytelność = możesz łatwo określić, które zmienne będą musiały zostać zainicjowane spoza klasy;
  3. zredukowane linie kodu = dla każdej inicjalizacji wykonanej w deklaracji będzie mniej linii w konstruktorze.
 3
Author: Marco Lackovic,
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.

 1
Author: Chandra Patni,
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.

 1
Author: jkeesh,
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.

 0
Author: fastcodejava,
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.

 0
Author: tulu,
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.

 0
Author: Andriy Kryvtsun,
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.

 0
Author: GeertVc,
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