1. Typowanie

Zmienne przechowujące typy referencyjne (klasy) można również konwertować na inne typy. Ale działa to tylko w ramach jednej hierarchii typów. Spójrzmy na prosty przykład. Załóżmy, że mamy następującą hierarchię klas, w której klasy poniżej dziedziczą klasy powyżej.

Typowanie

Typowanie typów referencyjnych, jak również prymitywnych, jest również klasyfikowane jako poszerzanie i zawężanie.

Widzimy, że klasa Cat dziedziczy klasę Pet, a klasa Pet z kolei dziedziczy klasę Animal.

Jeśli napiszemy taki kod:

Animal kitten = new Cat();

To jest rozszerzająca konwersja typu . Nazywa się to również rzutowaniem niejawnym. Rozszerzyliśmy odwołanie do kota , tak że teraz odnosi się ono do obiektu Cat . Przy takiej konwersji typu nie będziemy mogli używać odwołania do kotka do wywoływania metod obecnych w klasie Cat , ale nieobecnych w klasie Animal .

Konwersja zawężająca (lub jawna obsada) zachodzi w przeciwnym kierunku:

Cat cat = (Cat) kitten;

Wyraźnie zaznaczyliśmy, że chcemy rzucić odwołanie przechowywane w zmiennej kotka (której typem jest Animal ) na typ Kota .



2. Sprawdzenie typu obiektu

Ale tutaj trzeba być bardzo ostrożnym. Jeśli to zrobisz:

Animal beast = new Cat();
Wolf grayWolf = (Wolf) beast;

Kompilator zezwoli na ten kod, ale podczas uruchamiania programu wystąpi błąd ! JVM zgłosi wyjątek:

Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to a Wolf

Odwołania do obiektu Cat mogą być przechowywane tylko w zmiennych, których typ jest przodkiem klasy Cat: Pet, Animal lub Object.

Dlaczego?

Istotne jest tutaj to, że odwołanie do obiektu jest używane do odwoływania się do metod i zmiennych tego obiektu . I nie będzie żadnych problemów, jeśli użyjemy zmiennej Animal do przechowywania referencji do obiektu Cat: typ Cat zawsze ma zmienne i metody typu Animal — odziedziczył je!

Ale jeśli JVM pozwoli nam przechowywać odwołanie do obiektu Cat w zmiennej Wolf, możemy mieć sytuację, w której moglibyśmy spróbować użyć zmiennej grayWolf do wywołania metody, która nie istnieje w obiekcie Cat przechowywanym w tej zmiennej . Dlatego ten układ jest niedozwolony.

Java ma specjalny instanceofoperator, który pozwala sprawdzić, czy obiekt jest określonego typu i dlatego może być przechowywany w zmiennej określonego typu. Wygląda to dość prosto:

variable instanceof Type

Przykład:

Animal beast = new Cat();
if (beast instanceof Wolf)
{
   Wolf grayWolf = (Wolf) beast;
}

Ten kod nie spowoduje błędów — nawet w czasie wykonywania.

Oto kilka innych przykładów ilustrujących sytuację:

Poszerzająca konwersja typu Opis
Cow cow = new Whale();

Jest to klasyczna konwersja rozszerzająca — nie jest wymagany żaden operator konwersji typów. CowTeraz na obiekcie można wywołać tylko metody zdefiniowane w klasie Whale.

Na cowzmiennej kompilator pozwoli ci wywoływać tylko metody, które Cowma jego typ (klasa).

Konwersja typu zawężającego
Cow cow = new Whale();
if (cow instanceof Whale)
{
   Whale whale = (Whale) cow;
}
Klasyczna konwersja zawężająca: musisz dodać kontrolę typu i operator rzutowania.
Zmienna Cow cowprzechowuje referencję do Whaleobiektu.
Sprawdzamy, czy tak jest , a następnie przeprowadzamy (zawężoną) konwersję typu. Lub jak to się nazywa:
rzut typu
.

Cow cow = new Cow();
Whale whale = (Whale) cow; // Exception
Typ referencyjny można zawęzić bez sprawdzania typu obiektu.
Jeśli cowzmienna odnosi się do obiektu, który nie jest Whale, InvalidClassCastExceptionzostanie wygenerowany .


3. Wywołanie oryginalnej metody: słowo superkluczowe

Kiedy nadpisujemy metodę klasy nadrzędnej, czasami zamiast zastępować ją naszą własną, chcemy tylko nieznacznie ją uzupełnić.

Byłoby fajnie, gdybyśmy mogli zastosować metodę klasy nadrzędnej w naszej metodzie, a następnie wykonać część naszego własnego kodu. A może najpierw wykonaj własny kod, a następnie wywołaj metodę klasy nadrzędnej.

A Java pozwala nam właśnie na to. Aby wywołać metodę klasy nadrzędnej, wykonaj następujące czynności:

super.method(arguments);

Przykłady:

class PeaceTime
{
   public double getPi()
   {
      return 3.14;
   }
}

class WarTime extends PeaceTime
{
   public double getPi()
   {
      return super.getPi()*2;  // 3.14*2
   }
}

W czasie wojny wartość Pimoże być większa niż 6! Oczywiście żartujemy, ale ten przykład pokazuje, jak to wszystko może działać.

Oto kilka innych przykładów, aby trochę wyjaśnić sprawę:

Kod Opis
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("I'm a white whale");
   }

   public void printName()
   {
      System.out.println("I'm a cow");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.print("This is incorrect: ");
      super.printName();
      System.out.println("I'm a whale");
   }
}
Cowi Whalezajęcia
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printAll();
}
Wyjście ekranu będzie:
I'm a white whale
This is incorrect: I'm a cow
I'm a whale

To jest trudna sprawa. Szczerze mówiąc, jest to jedna z najtrudniejszych rzeczy w OOP . To powiedziawszy, musisz to wiedzieć i rozumieć.