Konstruktor obiektów danej klasy
Konstruktor to specjalna, publicznie dostępna metoda służąca do konstruowania obiektów danej klasy.
Nazwa konstruktora musi być identyczna z nazwą klasy - czyli w odróżnieniu od nazw pozostałych metod, które rozpoczynamy małymi literami, nazwę konstruktora rozpoczynamy od wielkiej litery, tak jak nazwę klasy. Ponadto konstruktor musi być publiczny i nie można dla niego zadeklarować żadnego typu zwracanej wartości (dotyczy to także typu void). Czyli deklaracja musi wyglądać tak, jak w następującym przykładzie:
class MyClass {
public MyClass() {
}
}
W dalszej części tego rozdziału konstruktory opisane są w kontekście dziedziczenia klas.
Aby przygotować się na te rozważania, zapoznaj się najpierw przynajmniej pobieżnie z rozdziałem Dziedziczenie klas.
Pusty konstruktor woła konstruktor klasy nadrzędnej - przodka (klasy, z której dziedziczy dana klasa) (ang. ancestor). Pamiętaj, że każda klasa dziedziczy pośrednio lub bezpośrednio od klasy java.lang.Object. Czyli w naszym przypadku klasa MyClass jest bezpośrednim potomkiem klasy java.lang.Object (ang. descendant). Czyli wywołanie konstruktora klasy MyClass spowoduje uruchomienie konstruktora klasy java.lang.Object bez parametru.
Jeśli jawnie utworzysz jakikolwiek konstruktor klasy podrzędnej (dziedziczącej), wówczas "zniknie" tworzony domyślnie konstruktor bezparametrowy, lecz mimo to w chwili wywołania jakiegokolwiek konstruktora zostanie zawsze najpierw wywołany bezparametrowy konstruktor klasy nadrzędnej, a dopiero w następnej kolejności zostanie wykonany kod zapisany w ciele jawnie utworzonego konstruktora klasy dziedziczącej (podrzędnej) bez względu na ilość i typ parametrów tego konstruktora.
Ilustruje to następujący przykład:
public class ConstructorTest {
public static void main( String[] args ) {
DerivedClass1 derivedClass1object = new DerivedClass1(); // calling sequence: AncestorClass()
System.out.println( "____________________________" );
DerivedClass2 derivedClass2object = new DerivedClass2(); // calling sequence: AncestorClass()
System.out.println( "____________________________" );
DerivedClass3 derivedClass3object = new DerivedClass3(); // calling sequence: AncestorClass() -> body of DerivedClass3() constuctor
System.out.println( "____________________________" );
//DerivedClass4 derivedClass4object = new DerivedClass4(); // compiler ERROR - there is no DerivedClass4() constructor
System.out.println( "____________________________" );
DerivedClass4 derivedClass4object = new DerivedClass4( 1 ); // calling sequence: AncestorClass() -> body of DerivedClass4( int param ) constructor
System.out.println( "____________________________" );
DerivedClass5 derivedClass5object = new DerivedClass5( 1 ); // calling sequence: AncestorClass() -> body of DerivedClass5( int param ) constructor
System.out.println( "____________________________" );
}
}
class AncestorClass {
public AncestorClass() {
System.out.println( "AncestorClass object CONSTRUCTED - () constructor called" );
}
public AncestorClass( int param ) {
System.out.println( "AncestorClass object CONSTRUCTED - ( int param ) constructor called" );
}
}
class DerivedClass1 extends AncestorClass {
}
class DerivedClass2 extends AncestorClass {
public DerivedClass2() {
}
}
class DerivedClass3 extends AncestorClass {
public DerivedClass3() {
System.out.println( "DerivedClass3 object CONSTRUCTED" );
}
}
class DerivedClass4 extends AncestorClass {
public DerivedClass4( int param ) {
System.out.println( "DerivedClass4 object CONSTRUCTED" );
}
}
class DerivedClass5 extends AncestorClass {
public DerivedClass5() {
System.out.println( "DerivedClass5 object CONSTRUCTED - () constructor called" );
}
public DerivedClass5( int param ) {
System.out.println( "DerivedClass5 object CONSTRUCTED - ( int param ) constructor called" );
}
}
Wynik:
AncestorClass object CONSTRUCTED - () constructor called
____________________________
AncestorClass object CONSTRUCTED - () constructor called
____________________________
AncestorClass object CONSTRUCTED - () constructor called
DerivedClass3 object CONSTRUCTED
____________________________
____________________________
AncestorClass object CONSTRUCTED - () constructor called
DerivedClass4 object CONSTRUCTED
____________________________
AncestorClass object CONSTRUCTED - () constructor called
DerivedClass5 object CONSTRUCTED - ( int param ) constructor called
____________________________
Zwróć uwagę na dwie kwestie:
- w metodzie main klasy ConstructorTest występuje "zakomentowany"
wiersz:
//DerivedClass4 derivedClass4object = new DerivedClass4(); // compiler ERROR - there is no DerivedClass4() constructor
W klasie DerivedClass4 zdefiniowany jest tylko jeden konstruktor z parametrem typu int: public DerivedClass4( int param )
Jak widać w takiej sytuacji ZNIKA konstruktor domyślny, lub inaczej mówiąc, nie jest tworzony domyślny konstruktor bezparametrowy.
- w wypadku obiektów klasy DerivedClass4 oraz DerivedClass5 zawsze najpierw jest
wołany bezparametrowy konstruktor klasy nadrzędnej AncestorClass, a
dopiero w następnej koleności jest wykonywane ciało wywoływanego konstruktora
klasy odziedziczonej (podrzędnej).
UWAGA! Zauważ, że w naszym ostatnim przykładzie wywoływany jest konstruktor z parametrami, a w klasie nadrzędnej AncestorClass jest także zdefiniowany taki sam konstruktor - konstruktor z taką samą liczbą parametrów i takimi samymi typami parametrów, a mimo tego wywoływany jest wyłącznie bezparametrowy konstruktor klasy nadrzędnej AncestorClass.
A teraz usuńmy konstruktora bezargumentowego w klasie AncestorClass...
Dla uproszczenia sytuacji, usuńmy wszystkie klasy DerivedClassX za wyjątkiem DerivedClass5.
Usuńmy też z metody main wszystkie odwołania do usuniętych klas - zostawmy konstruktor klasy DerivedClass5 z argumentem typu int.
Zwróć uwagę, że zmieniłem nazwę klasy głównej - dodałem dwójkę na końcu nazwy. Musisz oczywiście zapisać ten kod pod nazwą ConstructorTest2.java.
public class ConstructorTest2 {
public static void main( String[] args ) {
DerivedClass5 derivedClass5object = new DerivedClass5( 1 ); // calling sequence: AncestorClass() -> body of DerivedClass5( int param ) constructor
}
}
//****** UWAGA ****************
// W tej wersji klasa AncestorClass jest bez konstruktora bezargumentowego
class AncestorClass {
public AncestorClass( int param ) {
System.out.println( "AncestorClass object CONSTRUCTED - ( int param ) constructor called" );
}
}
class DerivedClass5 extends AncestorClass {
public DerivedClass5() {
System.out.println( "DerivedClass5 object CONSTRUCTED - () constructor called" );
}
public DerivedClass5( int param ) {
System.out.println( "DerivedClass5 object CONSTRUCTED - ( int param ) constructor called" );
}
}
Okazuje się, że jeśli w klasie nadrzędnej nie ma konstruktora bez argumentu, program się nie kompiluje.
Nie ma możliwości dziedziczenia z klasy, w której zdefiniowano konstruktor z argumentami, a nie zdefiniowano konstruktora bez argumentów. JESZCZE NIE WIEM DLACZEGO ISTNIEJE TAKIE OGRANICZENIE.
W przypadku konstruktorów warto wspomnieć o słowie kluczowym this (patrz Słowa kluczowe this oraz super). Pozwala ono odróżnić nazwy elementów związanych z daną klasą od takich samych nazw używanych jako lokalne np. parametry metody. Konstruktor jest szczególnym rodzajem metody. Często będziemy pisali tak:
public class Klasa {
private int a;
public Klasa( int a ) {
this.a = a; // this.a oznacza atrybut klasy int a, natomiast a oznacza argument/parametr konstruktora
}
}
W przypadku konstruktorów warto także wspomnieć o słowie kluczowym super (patrz Słowa kluczowe this oraz super). Pozwala ono wywołać metodę klasy nadrzędnej (ancestor class). Przykładowo można wywołać dowolny konstruktor klasy nadrzędnej albo dowolną inną metodę. Uwaga, aby z konstruktora klasy odziedziczonej wywołać konstruktor klasy nadrzędnej, konstrukcja super() lub super( <lista_parametrów>) musi być umieszczona jako pierwsza w ciele konstruktora wywołującego. W przypadku wszystkich innych metod możemy używać wywołania super.<nazwMetody> ( <lista_parametrów> ) w dowolnym miejscu kodu metody w klasie odziedziczonej.
Przykład:
public class SuperTest {
public static void main( String[] args ) {
DerivedClass derivedClassObject = new DerivedClass( 1 ); // calling sequence: AncestorClass() -> body of DerivedClass5( int param ) constructor
}
}
class AncestorClass {
public AncestorClass() {
System.out.println( "AncestorClass object CONSTRUCTED - () constructor called" );
}
public AncestorClass( int param ) {
System.out.println( "AncestorClass object CONSTRUCTED - ( int param ) constructor called" );
}
}
class DerivedClass extends AncestorClass {
public DerivedClass( int param ) {
super( param );
System.out.println( "DerivedClass object CONSTRUCTED - ( int param ) constructor called" );
}
}