CodeGym /Blog Java /Random-PL /Poszerzanie i zawężanie typów referencyjnych
Autor
John Selawsky
Senior Java Developer and Tutor at LearningTree

Poszerzanie i zawężanie typów referencyjnych

Opublikowano w grupie Random-PL
Cześć! Na poprzedniej lekcji omawialiśmy rzutowanie typów pierwotnych. Przypomnijmy pokrótce, o czym była mowa. Poszerzanie i zawężanie typów referencyjnych - 1Wyobrażaliśmy sobie typy prymitywne (w tym przypadku typy liczbowe) jako zagnieżdżone lalki, które różnią się rozmiarem w zależności od ilości zajmowanej pamięci. Jak zapewne pamiętasz, umieszczenie mniejszej lalki w większej jest proste zarówno w prawdziwym życiu, jak iw programowaniu w Javie.

public class Main {
   public static void main(String[] args) {
       int bigNumber = 10000000;
       short smallNumber = (short) bigNumber;
       System.out.println(smallNumber);
   }
}
To jest przykład automatycznej konwersji lub poszerzenia . Dzieje się to samo, więc nie musisz pisać dodatkowego kodu. W końcu nie robimy nic niezwykłego: po prostu wkładamy mniejszą lalkę do większej lalki. To inna sprawa, jeśli spróbujemy zrobić coś przeciwnego i włożyć większą rosyjską lalkę do mniejszej lalki. Nie możesz tego zrobić w prawdziwym życiu, ale w programowaniu możesz. Ale jest jeden niuans. Jeśli spróbujemy wstawić zmienną intdo shortzmiennej, sprawy nie potoczą się dla nas tak gładko. W końcu shortzmienna przechowuje tylko 16 bitów informacji, ale intzajmuje 32 bity! W rezultacie przekazywana wartość jest zniekształcona. Kompilator zwróci nam błąd („ Stary, robisz coś podejrzanego!'). Ale jeśli wyraźnie wskażemy typ, na który konwertujemy naszą wartość, przejdzie dalej i wykona operację.

public class Main {

   public static void main(String[] args) {

       int bigNumber = 10000000;

       bigNumber = (short) bigNumber;

       System.out.println(bigNumber);

   }

}
Właśnie to zrobiliśmy w powyższym przykładzie. Operacja została wykonana, ale ponieważ shortzmienna może pomieścić tylko 16 z 32 bajtów, ostateczna wartość jest zniekształcona i otrzymujemy liczbę -27008 . Taka operacja nazywana jest jawną konwersją lub zawężeniem .

Przykłady poszerzania i zawężania typów referencyjnych

Porozmawiajmy teraz o tych samych operatorach stosowanych nie do typów pierwotnych, ale do obiektów i zmiennych referencyjnych ! Jak to działa w Javie? To właściwie całkiem proste. Istnieją przedmioty, które nie są ze sobą powiązane. Logiczne byłoby założenie, że nie można ich wzajemnie przekonwertować, ani jawnie, ani automatycznie:

public class Cat {
}

public class Dog {
}

public class Main {

   public static void main(String[] args) {

       Cat cat = new Dog(); // Error!

   }

}
Tutaj oczywiście pojawia się błąd. Klasy Cati Dognie są ze sobą powiązane i nie napisaliśmy „konwertera”, aby przechodzić z jednej do drugiej. Ma sens, że nie możemy tego zrobić: kompilator nie ma pojęcia, jak przekonwertować te obiekty z jednego typu na drugi. Jeśli obiekty są ze sobą powiązane, cóż, to inna sprawa! Powiązane jak? Przede wszystkim poprzez dziedziczenie. Spróbujmy wykorzystać dziedziczenie do stworzenia małego systemu klas. Będziemy mieli wspólną klasę reprezentującą zwierzęta:

public class Animal {

   public void introduce() {

       System.out.println("I'm Animal");
   }
}
Wszyscy wiedzą, że zwierzęta mogą być udomowione (zwierzęta domowe) lub dzikie:

public class WildAnimal extends Animal {

   public void introduce() {

       System.out.println("I'm WildAnimal");
   }
}

public class Pet extends Animal {

   public void introduce() {

       System.out.println("I'm Pet");
   }
}
Weźmy na przykład kły — mamy psy domowe i kojoty:

public class Dog extends Pet {

   public void introduce() {

       System.out.println("I'm Dog");
   }
}



public class Coyote extends WildAnimal {

   public void introduce() {

       System.out.println ("I'm Coyote");
   }
}
Specjalnie wybraliśmy najbardziej podstawowe klasy, aby były łatwiejsze do zrozumienia. Tak naprawdę nie potrzebujemy żadnych pól, wystarczy jedna metoda. Spróbujmy wykonać ten kod:

public class Main {

   public static void main(String[] args) {

       Animal animal = new Pet();
       animal.introduce();
   }
}
Jak myślicie, co będzie wyświetlane na konsoli? Czy zostanie wywołana introducemetoda klasy Petlub klasa? AnimalSpróbuj uzasadnić swoją odpowiedź przed kontynuowaniem czytania. A oto wynik! Jestem zwierzakiem Dlaczego to dostaliśmy? To wszystko jest proste. Mamy zmienną nadrzędną i obiekt podrzędny. Przez pisanie,

Animal animal = new Pet();
rozszerzyliśmy referencjęPet i przypisaliśmy ją do Animalzmiennej . Podobnie jak w przypadku typów pierwotnych, typy referencyjne są automatycznie rozszerzane w Javie. Nie musisz pisać dodatkowego kodu, aby to się stało. Teraz mamy obiekt potomny przypisany do odniesienia nadrzędnego. W rezultacie widzimy, że wywołanie metody jest wykonywane na klasie potomnej. Jeśli nadal nie do końca rozumiesz, dlaczego ten kod działa, przepisz go prostym językiem:

Animal animal = new DomesticatedAnimal();
Nie ma z tym problemu, prawda? Wyobraź sobie, że to jest prawdziwe życie, a referencją jest po prostu papierowa etykieta z napisem „Zwierzę”. Jeśli weźmiesz tę kartkę papieru i przyczepisz ją do obroży dowolnego zwierzaka, wszystko będzie dobrze. W końcu każde zwierzę to zwierzę! Proces odwrotny — przechodzenie w dół drzewa dziedziczenia do potomków — zawęża się:

public class Main {

   public static void main(String[] args) {

       WildAnimal wildAnimal = new Coyote();

       Coyote coyote = (Coyote) wildAnimal;

       coyote.introduce();
   }
}
Jak widać, tutaj wyraźnie wskazujemy klasę, na którą chcemy przekonwertować nasz obiekt. Wcześniej mieliśmy WildAnimalzmienną, a teraz mamy Coyote, która jest niżej w drzewie dziedziczenia. Ma sens, że bez wyraźnego wskazania kompilator nie zezwoli na taką operację, ale jeśli typ podamy w nawiasach, to wszystko działa. Poszerzanie i zawężanie typów referencyjnych - 2Rozważ inny, bardziej interesujący przykład:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Animal(); // Error!
   }
}
Kompilator generuje błąd! Ale dlaczego? Ponieważ próbujesz przypisać obiekt nadrzędny do odniesienia podrzędnego. Innymi słowy, próbujesz zrobić coś takiego:

DomesticatedAnimal domesticatedAnimal = new Animal();
Cóż, może wszystko zadziała, jeśli jednoznacznie określimy typ, na który próbujemy dokonać konwersji? To zadziałało z liczbami — spróbujmy! :)

public class Main {

   public static void main(String[] args) {

       Pet pet = (Pet) new Animal();
   }
}
Wyjątek w wątku „main” java.lang.ClassCastException: Nie można rzucić zwierzęcia na zwierzaka Błąd! Kompilator tym razem nie nakrzyczał na nas, ale skończyło się na wyjątku. Znamy już przyczynę: próbujemy przypisać obiekt nadrzędny do referencji podrzędnej. Ale właściwie dlaczego nie możesz tego zrobić? Ponieważ nie wszystkie zwierzęta są zwierzętami udomowionymi. Stworzyłeś Animalobiekt i próbujesz przypisać go do Petzmiennej. Kojot jest również Animal, ale nie jest Pet. Innymi słowy, kiedy piszesz

Pet pet = (Pet) new Animal();
new Animal()może reprezentować dowolne zwierzę, niekoniecznie zwierzę domowe! Oczywiście twoja Pet petzmienna nadaje się tylko do przechowywania zwierząt domowych (i ich potomków), a nie dowolnego rodzaju zwierząt. Dlatego ClassCastExceptionutworzono specjalny wyjątek Java, , dla przypadków, w których wystąpił błąd podczas rzutowania klas. Przeanalizujmy to jeszcze raz, aby wszystko było jaśniejsze. Odniesienie do rodzica może wskazywać na instancje klasy potomnej:

public class Main {

   public static void main(String[] args) {

       Pet pet = new Pet();
       Animal animal = pet;

       Pet pet2 = (Pet) animal;
       pet2.introduce();
   }
}
Na przykład tutaj nie mamy problemów. Mamy Petobiekt, do którego odwołuje się Petzmienna. Później Animalodniesienie wskazywało na ten sam obiekt. Następnie konwertujemy animaldo Pet. Swoją drogą, dlaczego nam się to udało? Ostatnim razem mamy wyjątek! Ponieważ tym razem naszym oryginalnym obiektem jest Pet!

Pet pet = new Pet();
Ale w ostatnim przykładzie był to Animalobiekt:

Pet pet = (Pet) new Animal();
Nie można przypisać obiektu przodka do zmiennej potomnej. Możesz zrobić odwrotnie.
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION