+ 3

What is a practical use of casting on an object in Java? (Lesson Downcasting)

Here is the text from the lesson Downcasting from Java Intermediate. Upcasting You can cast an instance of a subclass to its superclass. Consider the following example, assuming that Cat is a subclass of Animal. Animal a = new Cat(); Java automatically upcasted the Cat type variable to the Animal type. Downcasting Casting an object of a superclass to its subclass is called downcasting. Animal a = new Cat(); ((Cat)a).makeSound(); This will try to cast the variable a to the Cat type and call its makeSound() method. I understand the upcasting part, since Cat is a subclass of Animal. But what is the practical use of downcasting an object? And In my code linked, animal a, b and c works as expected. However, if I initiated a new Animal d and downcasting it to a cat it gives error. I thought it forces (casting) class animal d to a class cat temporally, but the code shows me wrong. https://sololearn.com/compiler-playground/cytZMvF8biXf/?ref=app

31st Mar 2024, 2:18 AM
Wong Hei Ming
Wong Hei Ming - avatar
29 Réponses
+ 5
To break your chain of gaming examples, here is a different idea. A lunchbox can contain a food item. Every food has ingredients, but the Food abstract superclass does not know which ones, they are implemented by the subclasses. Lunchbox has this method to add a food item: public void fill(Food food) But when I use it, I pass a concrete subclass: lunchbox.fill(new Omlette()); Then it is automatically upcast to Food inside the Lunchbox class. https://sololearn.com/compiler-playground/c7EpXT6AErI5/?ref=app
1st Apr 2024, 12:14 PM
Tibor Santa
Tibor Santa - avatar
+ 3
Solo I don't get it. "d" is initiated as a Cat, what is the point to "casting" it as a Cat again? The same goes for "e". Also, I don't understand the use of "f". It initiated the same as "a", then "upcasting" to Animal but retain the Cat method? In what kind of situation we need this functionality?
31st Mar 2024, 1:54 PM
Wong Hei Ming
Wong Hei Ming - avatar
+ 3
Because of the Liskov substitution principle, we would declare object with the superclass type, but instantiate them with the subtype. https://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface The baby examples in the Sololearn lessons are pretty much useless to explain this concept. I made some additions to it. Think about a zoo with several animals, including a Whale and a Cat. What whales and cats have in common, is that they both need to eat, this is represented in the Animal superclass. (Of course you could also override specifically HOW they eat, in the subclass.) And in addition, whales can swim, and cats can purr. As long as you treat them as generic animals, they cannot do those things, only when you 'downcast' them to the specific type of animal. Also in my code I am showing a more modern approach to the scenario where you would actually need this feature: pattern matching based on the subtype. https://sololearn.com/compiler-playground/cmQmO28fPSQp/?ref=app
31st Mar 2024, 3:55 PM
Tibor Santa
Tibor Santa - avatar
+ 3
Tibor Santa Yeah, I remember it and that was just few weeks ago. I think the lesson structure and way of teaching caused the confusion. On chapter "More on Classes" it talks about Abstract Classes and Interfaces before Casting and Downcasting. One would think the later method introduced is a better way to go. In this case, it seems not. And those lessons are like "you can do something in this way" but doesn't tell which is a better method. The possible usage I can think of is the game Planescape: Torment, where the player character Nameless One started off as a fighter, and able to freely switch classes (thief and mage) if conditions met. When Nameless One being a mega and decide switch to a fighter, he "upcasting" as fighter? On second thought it may not. It is because when switching from mega to fighter he retains the memorized spells, so when switching back to mega those spells are immediately available. It seems like "ejection" and "injection?"
1st Apr 2024, 6:25 AM
Wong Hei Ming
Wong Hei Ming - avatar
+ 3
Learning the basic mechanics and capabilities of a language, is just the first step. Understanding how to use them best, is the art of software development. And there is no single ultimate truth there, more like there are certain trends and fashions. There are best practices, design patterns that describe how to put the pieces together, so that the result is working and also maintainable on the long term. And even these patterns change over time, as new language features are added and some old practices are obsoleted or lose their relevance. On top of learning the OOP patterns, learning functional Java can improve a lot on code quality. My favorite scholar who deals with this: Venkat Subramaniam. Watch his talks on Youtube, you will learn a lot.
1st Apr 2024, 7:25 AM
Tibor Santa
Tibor Santa - avatar
+ 3
zemiak, I don't know how you feel about Java, but doesn't your code already show how messy using a class can be? class Version1 { public static void main(String[] args) { System.out.println( System.getProperty( "java.version" )); System.out.println( Runtime.version() ); System.out.println( Runtime.version().version() ); } } Output: 22 22+35-2369 [22] In the third print out, Runtime.version().version()? What? From the "Runtime" class, initiated a "version" object, and call its "version" method? Wouldn't it be easier to create a global constant HashMap data structure and store all the information about the version? There are multiple thread talked about video on stop writing classes in Python. I think at the beginning of the video talked about this madness. https://sololearn.com/compiler-playground/c1rk18PiBd95/?ref=app
1st Apr 2024, 10:42 AM
Wong Hei Ming
Wong Hei Ming - avatar
+ 3
A valid example of downcasting I can think of is from a game Banished, a city-building strategy game. Where there are citizens, from child, labor to different profession, try to live in a new land. The game keep track of every citizen, from their birth until they die. If I'm the programmer, I would create a class Citizen, has a class attribute living_citizen to keep track how many people in town. And the only method is collect_food_to_home, which every person can do. Then each person is based on the subclass Child (with age attribute) and has no extra method since they can't do anything. Then every profession, such as student and labor, are "downcasted" and have their own "work" method. When a person died or a student got graduated, it triggers a job relocation to find the best job for every person, thus everyone got "re-downcasting." It is the example I can think of. However, I can't think of any upcasting I had seen. PS. Sorry for keep using games as example.
1st Apr 2024, 10:43 AM
Wong Hei Ming
Wong Hei Ming - avatar
+ 3
I know writing classes or not is a preference, and I'm not against it. However, the can become difficult to manage if many subclasses are made. Take the vehicles I mentioned, and we are writing classes about vehicles. Both Car and Airplane need a mechanism to steer the direction, let's call it Steering_Wheel. It controls left / right turn, and how it makes a horn. Under the Car, there are Private_Car and Racing_Car. And Racing_Car_F1, which under Racing_Car, the steering wheel has additional method, Gear_Shifting. Does it mean we need to create a subclass, Steering_Wheel_Racing_Car maybe? When it comes to airplane, the steer not only control banking left or right, also climb and descend. Do we add another subclass? Or actually it is a similar mechanism to control the rudder, but it doesn't horn? Creating more subclass to describe a single entity seems futile to me. In reality, a same object can have many slight variances, and it is not possible to subclass all of them.
2nd Apr 2024, 9:32 AM
Wong Hei Ming
Wong Hei Ming - avatar
+ 3
Hello everyone...🖐️😎 Everyone's here again. Wong Hei Ming, I'm sorry for not answering right away, I was busy with business. But, as they say, "Better late than never." So, you are asking what is the meaning of my definitions of d, e and f. As far as I understand from the topic of this lesson, this syntax can be useful in large projects so that the programmer can clearly see which of the objects belongs to which class, so as not to make a mistake in choosing the right function. However, when using any function, it is recommended to check its availability beforehand so as not to cause an error. Personally, I drew an analogy for myself with initializing variables that can be declared both implicitly (for example, var v;) and explicitly (int i; double d;), for example: "var v; (int)v".
3rd Apr 2024, 7:29 PM
Solo
Solo - avatar
+ 3
As for the second, main issue of this discussion, object c. You don't see the difference because you're redefining a function in the Cat class, but if you're adding other functions, then you'll need to access them that way. The first stage of creating a class: class Animal { public void roar(){ System.out.println("Grr..."); } } Main: Animal an = new Animal(); an.roar(); The second stage, create a subclass: class Cat extends Animal { public void purr(){ System.out.println("Purr..."); } } Main: Cat ct = new Cat(); ct.purr(); If cats could growl, then they would have to initialize the object in this way: Animal anCt = new Cat(); anCt.roar(); ((Cat)anCt).purr(); But cats can't growl like that, so let's create another subclass for the Cat class, (infraclass): class Tiger extends Cat {} Main: Tiger tgr = new Tiger(); tgr.roar(); tgr.purr(); Animal anTgr = new Tiger(); anTgr.roar(); ((Cat)anTgr).purr(); https://sololearn.com/compiler-playground/cV1wm38YjxjN/?ref=app
3rd Apr 2024, 7:33 PM
Solo
Solo - avatar
+ 3
I came across this video today by chance. "Always use interfaces" by Christopher Okhravi https://youtu.be/TkUhAbbRCAE?si=Ecmvtyc72iyfCit6 It is about this specific topic, designing class hierarchies and avoiding the pitfalls of subtyping. He explains very clearly, probably his other videos are equally interesting, but I literally just found it.
4th Apr 2024, 3:12 PM
Tibor Santa
Tibor Santa - avatar
+ 2
To better understand Liskov substitution principle, I searched it with my first language and found an interesting explanation (added as comment in the code. Maybe Bob_Li can expand the idea?) To simplify, imagine a company hire birds as courier, advertising "every bird can become a courier." Penguin, a subclass of Bird, stuck at the warehouse entrance blocking other bird to take-off and make delivery. It stuck because penguin cannot fly. To comply to the principle, any changes of the subclass (ex. Eagle, Sparrow, they can fly) should not return a result which the superclass is not expecting (ex. Penguin cannot fly.) In other words, what superclass can perform, subclasses should also able to perform. However, I can't think of any usage of upcasting. If I want a subclass capable to perform the same as superclass and has extra methods, wouldn't using an interface be a better idea?
1st Apr 2024, 3:31 AM
Wong Hei Ming
Wong Hei Ming - avatar
+ 2
in a situation where the object is assigned to its superclass, without casting it is not possible to access the subclass in this example to run methods which is in a subclass but not in a superclass. import java.util.*; public class MyClass { public static void main(String args[]) { Collection<Integer> source = new ArrayList<>(); source.add(0); source.add(1); // source.add(1,10); ( (List<Integer>) source).add(1,10); System.out.println( source); } }
1st Apr 2024, 3:39 AM
zemiak
+ 2
Wong Hei Ming I wrote it other times too that subclass inheritance is the worst OOP feature invented. Just my opinion. :) I agree with you that an interface can be a better idea. Also in this example, making the Animal class or the Bird abstract, would also be an improvement. Because every single instance would in reality be a specific species, there is no such thing out there in nature as a generic animal. :) Maybe it would even be better to understand the merits of upcasting and inheritance, if you think about modifying existing programs. Sometimes you have already a class hierarchy that you cannot change, but you need to expand it by adding new functionality. Subclassing can help in those cases. And one of the design intentions of Java was to make it more maintainable on the long term.
1st Apr 2024, 5:11 AM
Tibor Santa
Tibor Santa - avatar
+ 2
Wong Hei Ming inheritance gets complicated easily. Also, it is handled differently by different languages, so it gets even more confusing if you know two or more. C++ and Java have different approaches to virtual functions, for example. And I would not even try to add multiple inheritance into this mix. Interface and composition is the way to go and try to avoid inheritance if possible. if there is the need for casting, then you know your code is going to be a tangled mess. My approach to OOP is to use it as a simple data encapsulation type. No inheritance.
1st Apr 2024, 5:35 AM
Bob_Li
Bob_Li - avatar
+ 2
Bob_Li C++ and C are my next target, but I'm in the process of reviewing the learned languages. It is like rediscovering the known knowledge with new prospective (newly learned language.) It seems interface is a Lego, the core / foundation is the base class. Each pieces of Lego are a property or method. You can pre-build many pieces as you like and attach the one you need. Like a building, you can choose what color the wall is, and then select central / distribute air condition as you see fit.
1st Apr 2024, 6:26 AM
Wong Hei Ming
Wong Hei Ming - avatar
+ 2
Inheritance saves time, even standard APIs are built from interfaces, but they also have default methods and are implemented by abstract classes from which they are further inherited non abstract methods. The problem can be inappropriate, thoughtless architecture
1st Apr 2024, 7:17 AM
zemiak
+ 2
we should also mention Sealed classes, which as a new feature of the language allow inheritance to be desirably limited, which is probably a reaction to existing problems
1st Apr 2024, 7:34 AM
zemiak
+ 2
zemiak inheritance is fine if the hierarchy is kept shallow and the classes are kept simple. The problem is when the classes becomes big and bloated as you add more and more functionality to them. Then your code becomes fragile as the interdependence passed through inheritance becomes harder and harder to reason about. I feel that most latter additions to OOP are really stopgaps and patches for problems introduced in maintaing a purely OOP codebase. We basically created this complexity that necessitates additional checks and restraints. Code that started as Lego blocks often end up more like Jenga blocks.
1st Apr 2024, 7:34 AM
Bob_Li
Bob_Li - avatar
+ 2
Wong Hei Ming yes, the capsule is also a good example. :) On the other hand, modeling everything with subclasses, can lead to a crazy deathtrap, you have to draw a line where you want to stop adding more detail to your class hierarchy. JSON: [{ "capsule-type": "airplane", "capsule-attributes": { "manufacturer": "Airbus", "model": "A380", "cruise-speed": 903, "wingspan": 80 } },{ "capsule-type": "car", "capsule-attributes": { "manufacturer": "BMW", "model": "i7", "fuel": "electric", "horsepower": 448 } }]
2nd Apr 2024, 5:25 AM
Tibor Santa
Tibor Santa - avatar