Pytanie Jak wywołać jeden konstruktor z innego w Javie?


Czy możliwe jest wywołanie konstruktora z innego (w ramach tej samej klasy, a nie z podklasy)? Jeśli tak, to w jaki sposób? A jaki może być najlepszy sposób wywołania innego konstruktora (jeśli jest na to kilka sposobów)?


1828
2017-11-12 20:10


pochodzenie


sprawdź też: yegor256.com/2015/05/28/one-primary-constructor.html - yegor256
Uważam, że przesłanka twojego pytania jest błędna. Zamiast wywoływania konstruktora w konstruktorze, użyj wzoru Factory. Statyczna metoda fabryczna najpierw tworzy wszystkie obiekty niższego poziomu. Następnie konstruuje obiekty wyższego poziomu, które otrzymują zwrot z wywołania fabrycznego. Ta technika usuwa złożoność modelu, który ułatwia konserwację, klarowność i testowanie. - David Medinets


Odpowiedzi:


Tak to mozliwe:

public class Foo {
    private int x;

    public Foo() {
        this(1);
    }

    public Foo(int x) {
        this.x = x;
    }
}

Aby połączyć się z konkretnym konstruktorem superklasy zamiast jednego w tej samej klasie, użyj super zamiast this. Zauważ, że możesz łączyć się tylko z jednym konstruktorem, i musi to być pierwsze zdanie w twoim ciele konstruktora.

Zobacz też to powiązane pytanie, co jest o C #, ale gdzie obowiązują te same zasady.


2468
2017-11-12 20:12



Więc przypuszczam, że nie można wywołać super konstruktora i innego konstruktora tej samej klasy, ponieważ obie muszą być pierwszą linią? - gsingh2011
@ gsingh2011: Rzeczywiście. Możesz tylko połączyć się z jeden inny konstruktor. - Jon Skeet
To musi pojawić się w pierwszym wierszu, ale możesz wykonać obliczenia w konstruktorze przed jego wywołaniem: Możesz użyć metod statycznych w argumentach tego () w pierwszym wierszu i zawrzeć dowolne obliczenia, które muszą zostać wykonane przed wywołaniem do innego konstruktora w tej statycznej metodzie. (Dodałem to jako oddzielną odpowiedź). - Christian Fries
@ gsingh2011 Wiem, że jest późno, ale jak do tej pory można wywołać przeciążonego konstruktora za pomocą tego (...), a następnie w tym przeciążonym konstruktorze można wywołać konstruktor klasy podstawowej za pomocą super (...) - Ali
@ JustinTime: Ponownie, zależy to od tego, co masz na myśli przez "tworzenie" - obiekt jest "tworzony" w tym, że jego pamięć jest przydzielana, a typ jest ustawiany przed wykonaniem jakichkolwiek ciał konstruktora. Konstruktory to inicjalizacja, a nie tworzenie. W szczególności typ obiektu jest jego "ostatecznym" typem od samego początku - tak więc jeśli wywołasz jakiekolwiek wirtualne metody od konstruktorów, otrzymasz najbardziej specyficzne nadpisanie nazwane. Wierzę, że to różni się od C ++. - Jon Skeet


Za pomocą this(args). Preferowany wzór to praca od najmniejszego konstruktora do największego.

public class Cons {

 public Cons() {
  // A no arguments constructor that sends default values to the largest
  this(madeUpArg1Value,madeUpArg2Value,madeUpArg3Value);
 }

 public Cons(int arg1, int arg2) {
  // An example of a partial constructor that uses the passed in arguments
  // and sends a hidden default value to the largest
  this(arg1,arg2, madeUpArg3Value);
 }

 // Largest constructor that does the work
 public Cons(int arg1, int arg2, int arg3) {
  this.arg1 = arg1;
  this.arg2 = arg2;
  this.arg3 = arg3;
 }
}

Możesz również użyć ostatnio zalecanego podejścia valueOf lub po prostu "of":

public class Cons {
 public static Cons newCons(int arg1,...) {
  // This function is commonly called valueOf, like Integer.valueOf(..)
  // More recently called "of", like EnumSet.of(..)
  Cons c = new Cons(...);
  c.setArg1(....);
  return c;
 }
} 

Aby zadzwonić do super klasy, użyj super(asdf). Wywołanie super musi być pierwszym wywołaniem w konstruktorze lub pojawi się błąd kompilatora.


198
2018-03-11 20:33



Jeśli używanych jest wiele parametrów konstruktora, należy rozważyć konstruktora. Zobacz artykuł 2 "Efektywna Java" autorstwa Joshua Blocha. - koppor
Problem z realizacją ostatniego podejścia z wykorzystaniem metody fabrycznej, newCons, jest to, że próbujesz zmienić stan obiektu, używając setArg1(...), to najprawdopodobniej ma swoje pola ustawione jako ostateczne. Ponieważ staramy się zachować jak najwięcej obiektu niezmiennego, jeśli nie całkowicie, wzorzec budowniczego poprawi ten problem bardziej poprawnie. - YoYo
Czy nie wolałbyś robić :: public Cons () {this (madeUpArg1Value, madeUpArg2Value); } - LordHieros
Inicjalizacja powinna przebiegać od najmniejszej do największej - nigdy nie będę miał domyślnego konstruktora wywołującego łańcuch do konstruktora wieloparametrowego. Musi się zdarzyć, że wszystkie konstruktory wywołają wartość domyślną lub konstruktor o mniejszej liczbie parametrów. - Rodney P. Barbati
@ RodneyP.Barbati W języku Java bardzo często używane są konstruktory o niższej sile do wywoływania konstruktorów o większej dokładności a następnie nie robić nic innego. jeśli klasa K ma, na przykład, dwa ostatnie pola a, b, wówczas byłby "generalny konstruktor" K(A a, B b) { this.a = a; this.b = b; }. A następnie, jeśli b ma rozsądną wartość domyślną, może istnieć konstruktor jednoargumentowy K(A a) { this(a, DEFAULT_B); }i jeśli jest domyślny a jak również mamy domyślny konstruktor: K() { this(DEFAULT_A); }. To dość powszechna konwencja w Javie. - Joshua Taylor


[Uwaga: Chcę tylko dodać jeden aspekt, którego nie widziałem w innych odpowiedziach: jak pokonać ograniczenia wymogu, że to () musi znajdować się w pierwszym wierszu).]

W Javie inny konstruktor tej samej klasy może być wywołany z konstruktora przez this(). Pamiętaj jednak, że this musi być w pierwszej linii.

public class MyClass {

  public MyClass(double argument1, double argument2) {
    this(argument1, argument2, 0.0);
  }

  public MyClass(double argument1, double argument2, double argument3) {
    this.argument1 = argument1;
    this.argument2 = argument2;
    this.argument3 = argument3;
  }
}

Że this musi pojawić się w pierwszym wierszu wygląda na duże ograniczenie, ale możesz konstruować argumenty innych konstruktorów za pomocą metod statycznych. Na przykład:

public class MyClass {

  public MyClass(double argument1, double argument2) {
    this(argument1, argument2, getDefaultArg3(argument1, argument2));
  }

  public MyClass(double argument1, double argument2, double argument3) {
    this.argument1 = argument1;
    this.argument2 = argument2;
    this.argument3 = argument3;
  }

  private static double getDefaultArg3(double argument1, double argument2) {
    double argument3 = 0;

    // Calculate argument3 here if you like.

    return argument3;

  }

}

177
2018-04-23 23:12



To prawda, że ​​możesz wywoływać metody statyczne w ten sposób, aby wykonywać skomplikowane obliczenia wartości argumentów, co jest w porządku. Jednakże, jeśli ktoś uważa, że ​​kod jest potrzebny przed delegowaniem konstruktora (this(...)) wtedy rozsądnie byłoby założyć, że gdzieś popełniono straszny błąd i że projekt może być trochę przemyślany. - Engineer Dollery
Zgodziłbym się, że bardzo złożona transformacja prawdopodobnie wskazuje na problem projektowy. Ale 1) są pewne proste przekształcenia, dla których może to być użyteczne - nie wszyscy konstruktorzy są tylko liniową projekcją na innych i 2) może być inna sytuacja, w której ta informacja mogłaby stać się ręką, jak wspieranie legacy kodu. (Chociaż zgadzam się z pańskim wnioskiem, nie rozumiem, dlaczego uzasadniałoby to głosowanie w dół). - Christian Fries
Jest to całkowicie przeciwne do tego, co sugerowałbym - konstruktor bez parametrów powinien zainicjować wszystkie wartości do wartości domyślnych. Konstruktor 2parametru powinien wywołać konstruktor bez paramu, a następnie zainicjować 2 wartości, które otrzymuje. Konstruktor 3 parametrów powinien wywołać konstruktor 2 parametrów, a następnie zainicjować trzecią wartość na wartość, którą otrzymuje. Wykonanie go w przedstawiony sposób oznacza, że ​​musisz wykonać znacznie więcej pracy, aby dodać kolejny parametr. - Rodney P. Barbati
@ RodneyP.Barbati: Widzę kilka problemów w sposobie, w jaki to opisujesz: a) Robiąc to w ten sposób, nie można zilustrować użycia metody statycznej w konstruktorze (co jest intencją tego przykładu); -) i b) jeśli zrobisz to po swojemu, pola nie mogą być final (końcowe pola można zainicjować tylko raz). - Christian Fries
@ RodneyP.Barbati: Dwa inne aspekty: c) Uważam, że zawsze należy inicjalizować obiekt w jednym punkcie, który musi być najbardziej ogólnym konstruktorem. Jeśli inicjalizacja obiektu wymaga złożonego zadania (obiekt nie jest leniwy) lub sprawdzenia lub pozyskania pewnych zasobów (jak plik), to lubisz to robić tylko raz. I d) Dodanie kolejnego argumentu (np. Argumentu4), dla którego inicjalizacja zależy od wartości argumentu1 do argumentu3, musiałbyś zmienić wszystkie konstruktory w twoim przypadku, podczas gdy tutaj musisz tylko dodać jeden i pozwolić 3-arg wywołać 4 -arg konstruktor. - Christian Fries


Kiedy muszę wywołać innego konstruktora z wewnątrz kodu (nie w pierwszym wierszu), zwykle używam metody pomocniczej, takiej jak ta:

class MyClass {
   int field;


   MyClass() {
      init(0);
   } 
   MyClass(int value) {
      if (value<0) {
          init(0);
      } 
      else { 
          init(value);
      }
   }
   void init(int x) {
      field = x;
   }
}

Ale najczęściej próbuję to zrobić na odwrót, wywołując bardziej złożone konstruktory z prostszych na pierwszej linii, w miarę możliwości. Dla powyższego przykładu

class MyClass {
   int field;

   MyClass(int value) {
      if (value<0)
         field = 0;
      else
         field = value;
   }
   MyClass() {
      this(0);
   }
}

36
2018-05-26 15:09





W obrębie konstruktora możesz użyć this słowo kluczowe do wywołania innego konstruktora w tej samej klasie. Zrobienie tego nazywa się jawne wywołanie konstruktora.

Oto kolejna klasa Rectangle, z inną implementacją niż w sekcji Obiekty.

public class Rectangle {
    private int x, y;
    private int width, height;

    public Rectangle() {
        this(1, 1);
    }
    public Rectangle(int width, int height) {
        this( 0,0,width, height);
    }
    public Rectangle(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }

}

Ta klasa zawiera zestaw konstruktorów. Każdy konstruktor inicjuje niektóre lub wszystkie zmienne składowe prostokąta.


23
2018-05-07 22:52



dlaczego nie nazwiesz drugiego konstruktora, który jest Rectangle(int width, int height) w Rectangle() zamiast Rectangle(int x, int y, int width, int height) ? - ANjaNA
Domyślny konstruktor nie powinien posiadać wiedzy na temat konstruktorów wyższego poziomu - jest to ustawienie domyślne. Wykonanie tego wzorca spowoduje konieczność zmiany jednego lub więcej istniejących konstruktorów po dodaniu nowego. Na przykład dodaj wartość lineWidth i zobacz, co mam na myśli. Ale domyślnie zainicjuj wszystkie wartości i odwróć łańcuch konstruktora, zobaczysz, że każdy konstruktor buduje na poprzednim i inicjuje tylko wartości, które obsługuje - Możesz dodać nowy bez zmiany istniejących. Istnieje wiele typowych wzorców w języku Java, które nie są dobrymi wzorami. - Rodney P. Barbati


Jak już wszyscy mówili, używasz this(…), który nazywa się jawne wywołanie konstruktora.

Pamiętaj jednak o tym w ramach takiego jawnego instrukcji wywoływania konstruktora możesz nie odwoływać się

  • każdy zmienne instancji lub
  • każdy metody instancji lub
  • każdy klasy wewnętrzne zadeklarowane w tej klasie lub dowolnej nadklasie, lub
  • this lub
  • super.

Jak podano w JLS (§8.8.1.1).


12
2017-11-21 13:14





Powiem ci łatwą drogę

Tam są dwa typy konstruktorów:

  1. Domyślny konstruktor
  2. Sparametryzowany konstruktor

Wyjaśnię w jednym przykładzie

class ConstructorDemo 
{
      ConstructorDemo()//Default Constructor
      {
         System.out.println("D.constructor ");
      }

      ConstructorDemo(int k)//Parameterized constructor
      {
         this();//-------------(1)
         System.out.println("P.Constructor ="+k);       
      }

      public static void main(String[] args) 
      {
         //this(); error because "must be first statement in constructor
         new ConstructorDemo();//-------(2)
         ConstructorDemo g=new ConstructorDemo(3);---(3)    
       }
   }                  

W powyższym przykładzie pokazałem 3 rodzaje połączeń

  1. wywołanie this () musi być pierwszym stwierdzeniem w konstruktorze
  2. To jest Nazwa mniej Object. to automatycznie wywołuje domyślny konstruktor. 3. To wywołuje konstruktor sparametryzowany.

Uwaga: to musi być pierwsze stwierdzenie w konstruktorze.


7
2017-11-27 19:01



W głównej metodzie są następujące: //to(); błąd, ponieważ "musi być pierwszą instrukcją w konstruktorze  To stwierdzenie nie ma większego sensu. Jeśli próbujesz to powiedzieć to() nie można wzywać od wewnątrz Główny metoda, a następnie tak, nie może być, ponieważ main jest statyczny i nie ma odniesienia do to() - S R Chaitanya
to właśnie przekazuję ... co nowego w swoim komentarzu @SRChaitanya? - Shivanandam Sirmarigari
że źle to przekazujesz - Kevin Van Dyck