Java dla Testerów 18. Słowo kluczowe this

W tej lekcji pojawi się słowo kluczowe this, które przewinęło się już w poprzedniej lekcji mimochodem. Na przykładach pokażę Ci do czego może służyć i jak z niego korzystać.

Słowo kluczowe this: linki i materiały

Słowo kluczowe this jest odniesieniem do bieżącego obiektu, czyli np. obiektu którego metodę właśnie wywołujemy. Za pomocą this możemy się odnieść np. do pól, metod czy konstruktorów klasy danego obiektu.

Pola klasy

Chyba najczęstszym (zwłaszcza na początku) przypadkiem, w którym użyjemy słówka this, będzie odniesienie do pól klasy np. w konstruktorze albo setterze. Weźmy poniższy przykład:

public class Address {
    private String street;
    private String apartment;
    private String city;

    public Address(String cityParameter, String streetParameter, String apartmentParameter) {
        street = streetParameter;
        apartment = apartmentParameter;
        city = cityParameter;
    }
}

To, co się tutaj pewnie rzuca w oczy, to ten dziwny zabieg z dodaniem „Parameter” na końcu nazw parametrów, żeby nie było kolizji nazw z nazwami pól klasy. Działać działa, ale wygląda dziwnie, no bo przecież przekazujemy np. ulicę w parametrze, żeby przypisać ją do pola klasy reprezentującego ulicę. Więc jakby logicznie i parametr i pole klasy reprezentują podobną cząstkę informacji, więc takie wymyślanie na siłę innej nazwy jest trochę pozbawione sensu, jeżeli mamy inne rozwiązanie. A mamy i jest nim właśnie słowo kluczowe this. Spójrz na poniższy przykład już ze słowem this.

public class Address {
    private String street;
    private String apartment;
    private String city;

    public Address(String city, String street, String apartment) {
        this.street = street;
        this.apartment = apartment;
        this.city = city;
    }
}

No i to już wygląda dużo lepiej. „Gołe” nazwy to parametry (street, apartment, city). Natomiast te ze słowem this, to odniesienia do pól klasy (this.street, this.apartment, this.city). Czyli tak jak wspomniałam na początku, poprzez słówko this odnosimy się do bieżącego obiektu, a potem wskazujemy pole. A czym jest bieżący obiekt na tym przykładzie? No na tym przykładzie to żaden, bo tutaj mamy tylko klasę, obiektu jeszcze nie widzieliśmy. Ale gdy wywołamy sobie ten konstruktor np. w jakiejś innej klasie, by stworzyć obiekt tej klasy, to wtedy słowo this w tym konstruktorze, będzie się odnosić do właśnie tworzonego obiektu.

Konstruktory

Słowa this możemy także użyć, gdy będziemy się chcieli odwołać do konstruktora. Załóżmy, że do naszej klasy dodamy nowe pole. Konstruktor, który już mamy zostawimy jak jest, ale będziemi chcieli stworzyć kolejny konstruktor, który w którym będziemy przekazywali do pól miasto, ulicę i numer budynku ale właśnie także nasze nowe pole, czyli kod pocztowy.

public class Address {
    private String street;
    private String postalcode;
    private String apartment;
    private String city;

    public Address(String city, String street, String apartment) {
        this.street = street;
        this.apartment = apartment;
        this.city = city;
    }

    public Address(String city, String street, String apartment, String postalcode) {
        this.street = street;
        this.apartment = apartment;
        this.city = city;
        this.postalcode = postalcode;
    }
}

To, co może się tutaj rzucać w oczy to powtórzenia. Nasz nowy konstruktor jest właściwie powtórzeniem poprzedniego konstruktora i dodatkowo obsługujemy jeszcze jedno pole. No i właśnie słówka this możemy użyć, żeby tych powtórzeń uniknąć.

public class Address {
    private String street;
    private String postalcode;
    private String apartment;
    private String city;

    public Address(String city, String street, String apartment) {
        this.street = street;
        this.apartment = apartment;
        this.city = city;
    }

    public Address(String city, String street, String apartment, String postalcode) {
        this(city, street, apartment);
        this.postalcode = postalcode;
    }
}

W powyższym przykładzie pisząc this(city, street, apartment);odwołujemy się do trójparametrowego konstruktora i inicjalizujemy trzy pierwsze pola. Dopiero potem dodajemy jeszcze linijkę, w której inicjalizujemy kod pocztowy. W przypadku takiego użycia this należy pamiętać, że odwołanie do innego konstruktora musi się znaleźć na początku, przed innymi akcjami (czyli np. inicjalizacją pola postalcode).

Return this

Na koniec użycie, które może nam się przydać na przykład, gdy będziemy już budować swoje testy w Selenium w oparciu o POM (Page Object Model). Chodzi o metody, w których będziemy zwracali bieżący obiekt.

public class ProductPage extends BasePage{
    public String productName;
    public String productPrice;

    protected ProductPage addToCart(){
        System.out.println("Produkt dodany do koszyka");
        return this;
    }
    protected ProductPage setAmount(int amount){
        System.out.println("Wybrano ilość: " + amount);
        return this;
    }

    protected CartPage goToCart(){
        System.out.println("Przejście do koszyka");
        return new CartPage();
    }
}

W metodach addToCart oraz setAmount zwracamy obiekt klasy ProductPage. Mówi o tym „ProductPage” zaraz po modyfikatorze dostępu. Na końcu jednak widzimy return this, a nie na przykład return new ProductPage().

To drugie zwróciłoby nam nowo stworzony obiekt klasy ProductPage. Natomiast konstrukcja return this nie zwróci nowego obiektu, zwróci bieżący obiekt danej klasy. Czyli przykładowo, żeby w klasie testowej wywołać metodę addToCart musimy najpierw stworzyć obiekt klasy ProductPage i na nim wywołać tę metodę. I ta metoda zwróci właśnie ten obiekt, a nie nowy obiekt ProductPage jak zrobiłaby to konstrukcja return new ProductPage().

Zarówno return new ProductPage() jak i return this pozwala nam na coś, co w starożytnym języku ponglish nazywamy chainowaniem. Poszczególne akcje możemy zatem połączyć w łańcuch, jak poniżej, ponieważ zwracany jest obiekt na klasy, na której możemy wykonać kolejną metodę.

public class Tests {
    @Test
    public void addToCartTest(){
        ProductPage productPage = new ProductPage();
        productPage.setAmount(3).addToCart().goToCart();
    }
}

Gdyby nasze metody nie zwracały nic (void) to każdą z metod musielibyśmy wykonać w osobnej linii na obiekcie, do którego referencję przechowuje zmienna productPage:

public class Tests {
    @Test
    public void addToCartTest(){
        ProductPage productPage = new ProductPage();
        productPage.setAmount(3);
        productPage.addToCart();
        productPage.goToCart();
    }
}

Sama wolę tworzyć nowe obiekty w metodach niż używać this. Tak na wszelki wypadek, bo bezpieczniej jest sobie stworzyć nowy, „świeży” obiekt, niż pracować na obiekcie, który z jakichś powodów może mieć nieprawidłowy stan. Niemniej, pokazuję także opcję z this, bo wchodząc w już istniejący projekt, możesz zobaczyć właśnie coś takiego.