I'm trying to figure out whether implementing individual methods of a subclass in an abstract superclass, or casting is the better way to go about the following scenario.
Suppose I have an abstract class Animal which has two subclasses, Dog and Cat and a Main class where I save objects of Dog and Cat in an Animal array. The following is how I would go about using methods of the subclasses in a more generalized array.
class Main{
public static void main(String[] args){
Animal[] animalArray = new Animal[2];
animalArray[0] = new Cat();
animalArray[1] = new Dog();
for (Animal a : animalArray){
if (a.getClass().equals(Dog.class){
((Dog)a).bark();
} else {
((Cat)a).meow();
}
}
}
}
However a friend suggested that casting isn't best practice, and that I should define each method in the abstract superclass in the following way:
public abstract class Animal{
public abstract String meow(){
return null;
}
public abstract String bark();
return null;
}
}
After setting the return values of these methods to null I would need to use #Override and implement them in the respective subclasses.
Which way is better? I'm afraid the abstract class will be too large and will have methods assigned to subclasses that don't make sense (even if all they do is return null). I think by using casting I can make more precise uses of the methods.
meow() and bark() shouldn't be defined in the Animal class. These methods are specific to Cat and Dog classes.
You should define an abstract method as shown below, in the Animal class and override it in the sub classes.
public abstract class Animal {
public abstract String action() {};
}
public class Dog extends Animal {
#Override
public String action() {
//your implementation (bark)
}
}
public class Cat extends Animal {
#Override
public String action() {
//your implementation (meow)
}
}
Hope it answers your query.
Related
It's hard to properly ask, so I will create an example:
class Animal {
public void doSomething() {
}
}
class Dog extends Animal {
}
class Cat extends Animal {
}
class Sheltie extends Dog {
#Override
public void doSomething() {
super.doSomething();
System.out.println("Exactly the same");
}
}
class Abbysian extends Cat {
#Override
public void doSomething() {
super.doSomething();
System.out.println("Exactly the same");
}
}
Is there a way in this case that I can override the doSomething() method in both Sheltie and Abbysian without duplicating a code and even typing #Override public void doSomething(). On top of that, I'd actually like to call super.doSomething() as well. I cannot change inheritance at all.
If those classes extended directly Animal I would create a class "between" them. I was thinking of using interface with default method but that didn't help as well.
Once you broke out into Cats and Dogs you discovered the limitations of class hierarchy - eventually you'll hit a point of no return.
Thankfully there is a thing called an Entity Component System (ECS) that is commonly used when you want to mish-mash functionality amongst many different entities.
A cat can wag its tail. A dog can wag its tail... but not all animals have tails!
So you register a component called Tail to the tailed animal entities.
I found this on the StackExchange code review group. https://codereview.stackexchange.com/questions/163215/entity-component-system-ecs
Decorator pattern can do this trick but it would be applied to specific instances of Sheltie / Abyssinian not to these classes:
class AnimalDecorator extends Animal {
protected Animal animal;
public AnimalDecorator(Animal animal) {
this.animal = animal;
}
#Override
public void doSomething() {
animal.doSomething(); // instead of call to super.doSomething
System.out.println("Exactly the same");
}
}
class Sheltie extends Dog { // doSomething not overridden
}
class Abyssinian extends Cat { // doSomething not overridden
}
// decorated instances
Animal sheltie = new AnimalDecorator(new Sheltie());
Animal abyssinian = new AnimalDecorator(new Abyssinian());
sheltie.doSomething();
abyssinian.doSomething();
I'm using Java 8 / Java 11. I have a type hierarchy (basically dtos or Java Beans) like
public abstract class Animal {
public abstract String getName();
public abstract int getAge();
}
And some imeplementations providing additional properties:
public class Dog extends Animal {
// implementation of abstract methods from base class animal
// additional properties
public String getSound() {
return "woof";
}
}
public class Dog extends Animal {
// implementation of abstract methods from base class animal
// additional properties
public String getSound() {
return "miaow";
}
}
public class Fish extends Animal {
// implementation of abstract methods from base class animal
// no implementaion for "getSound()"
}
Now, I'd like to process a Collection of Animals in a uniform way, e.g.
animals.forEach(x -> {
System.out.println(x.getName()); // works
System.out.println(x.getSound(); // doesn't work, as Fish is missing the method
});
I was wondering, what would be a good way to implement the "missing" methods assuming that they should return a default value like "n/a" for a String.
One obvious way would be to move all the missing methods to the base class and either declare them abstract or provide a default implementation.
But I'd like to have them more separate, i.e. making clear which properties were added for the "uniform processing".
Another way would be to introduce a helper class using instance of to determine, if the method is missing:
public class AnimalHelper {
public static String getSoundOrDefault(Animal animal) {
if (animal instanceof Dog) {
return ((Dog)animal).getSound();
}
if (animal instanceof Cat) {
return ((Cat)animal).getSound();
}
return "n/a";
}
}
which then gets called with an Animal:
System.out.println(AnimalHelper.getSoundOrDefault(animal));
This works, but the caller must now which methods to call on Animal directly and for which methods to use the helper.
Another solution, I came up with the adding an interface AnimalAdapter using the Java 8 feature of default implementation:
public interface AnimalAdapter {
default String getSoundOrDefault() {
return "n/a";
}
}
And adding it to the Animal class:
public abstract class Animal implements AnimalAdapter {
...
which results in adding the getSoundOrDefault() method in Dog and Cat, but not Fish:
public class Dog extends Animal {
...
#Override
public String getSoundOrDefault() {
return getSound();
}
}
(likewise in Cat).
Any comments on the above considerations or other ideas would be highly appreciated.
All what you have mentioned above as solutions are really good. But I take advantage to add more one solution based on polymorphism technic, and I think it's more simple and less expensive in terms of code.
Simply I'm gonna use Object.toString() method to display all needed parameters, so first of all you have to #Override toString() method as follow:
public class Dog extends Animal {
// implementation of abstract methods from base class animal
// additional properties
public String getSound() {
return "woof";
}
#Override
public String toString() {
return getName() + "\n" + getSound();
}
}
public class Fish extends Animal {
// implementation of abstract methods from base class animal
// no implementaion for "getSound()"
#Override
public String toString() {
return getName() + "\n" + "n/a";
}
}
public class Main {
public static void main(String[] args) {
Collection<Animal> animals = new ArrayList<>(2);
animals.add(new Dog());
animals.add(new Fish());
animals.forEach(System.out::println);
}
}
And here is the result:
What's definitely the best way to call a different method of a class according to a different object type in a List?
common example:
public class Dog extends Animals{
...
public void say(){
System.out("i'm a dog");
}
public class Cat extends Animals{
...
public void say(){
System.out("i'm a cat");
}
public class OtherClass {
public void route(){
List<Animals> aList = new ArrayList<>();
a.add(new Dog());
a.add(new Cat());
for(Animals a:aList)
methodOverloaded(a); ---> that's the point <---
}
public methodOverloaded(Dog d){
d.say();
}
public methodOverloaded(Cat c){
c.say();
}
}
Of course, the metaphorical goal is print I'm a dog on the first iteration and I'm a cat on second running methodOverloaded().
I tried
Visitor Pattern
instanceOf()
but I'm looking for better solution.
edit : I stricrtly want to call the overloaded methods of example's OtherClass.
The best way is to define abstract method in Animal and override it in child classes. That's how the polymorphism works.
No need of overloadings.
public abstract class Animal {
public abstract void say();
}
public class Dog extends Animal {
#Override
public void say() {
System.out.println("Bark");
}
}
public class Cat extends Animal {
#Override
public void say() {
System.out.println("Meow");
}
}
Usage:
public class Main() {
public static void main(String[] args) {
List<Animals> aList = new ArrayList<>();
a.add(new Dog());
a.add(new Cat());
for (Animals a : aList)
a.say();
}
}
Output:
Bark
Meow
____UPDATE_1
I would like to add some comments why the overloading these methods is not a good idea.
If you will add the following to your code - it will compile:
public methodOverloaded(Animal a) {
a.say();
}
But it will not work as you're expecting. It will call public methodOverloaded(Animal a) for all the elements of List.
Why does this happen?
For all iterations of the loop, the compile-time type of the parameter is Animal. The runtime type is different in each iteration, but this does not affect the choice of overloading. Because the compile-time type of the parameter is Animal, the only applicable overloading is the third one.
The behavior of this program is counterintuitive because selection among overloaded methods is static, while selection among overridden methods is dynamic.
The correct version of an overridden method is chosen at runtime, based on the runtime type of the object on which the method is invoked.
This can be fixed with:
public methodOverloaded(Animal a) {
if (a instanceof Cat) ? "Meow" :
(a instanceof Dog) ? "Bark" : "Unknown Animal"
}
Of course suggested option with overriding methods demonstrates better approach and more clean code.
Also, a safe, conservative policy is never to export two overloadings with
the same number of parameters, because it can confuse the client of the API. You can always give methods different names instead of overloading them.
But there is the case when at least one corresponding formal parameter in each pair of overloadings has a “radically different” (when it's clearly impossible to cast an instance of either type to the other) type in the two overloadings.
For example, ArrayList has one constructor that takes an int and a second constructor that takes a Collection. It is hard to imagine any confusion over which of these two constructors will be invoked under any circumstances.
You need to define the method in Animal and make it abstract
abstract class Animal {
public abstract void say();
}
this way, you can override this method in each child of Animal, and all you got to do is a.say()
each object will call their respective method.
You can do it like this:
for(Animals a:aList){
if(a instanceof Dog){
methodOverloaded((Dog) a);
}else if(a instanceof Cat){
methodOverloaded((Cat) a);
}
}
But according to the scenario you have described in the question, #J-Alex answer is a good way to go.
I can show you how "Factory Design pattern" is the suitable way here.
Define you main class like:
public abstract class Animal {
public abstract void say();
}
public class Dog extends Animal {
#Override
public void say() {
System.out.println("Bark");
}
}
public class Cat extends Animal {
#Override
public void say() {
System.out.println("Meow");
}
}
public class FactoryClass{
public static Animal getCorrectAnimal(String type){
if("Cat".equalsIgnoreCase(type)) return new Cat();
else if ("Dog".equalsIgnoreCase(type)) return new Dog();
return null;
}
}
public class TestFactoryClass {
public static void main(String[] args) {
Animal an = ComputerFactory.getCorrectAnimal("Cat");
List<Animals> aList = new ArrayList<>();
a.add(FactoryClass.getCorrectAnimal("Dog"));
a.add(FactoryClass.getCorrectAnimal("Cat"));
for (Animals a : aList)
a.say();
}
}
}
Trust me, if you will analyse the level of Abstraction here, it is awesome. The client/consumer never has to know the Dog or Cat class, he/she just has to know the type and a general abstract class Animal. You can even get rid of type here if you use one higher level of abstraction; you can read "Abstract Factory Design" for that. In this way, you expose the least of your class features (like here you exposed Dog and Cat class by using them directly with new in main class). Please upvote if you are satisfied.
I mean if we want to rewrite all the content of the method in the child class and change its function, why couldn't we just write a new method with a different name? What's the difference? I know this is a simple question. But I am new to programming and very confused. THX.
It's about giving different behaviour to common functionality for that type of thing, for example let's assume all Vehicles beep, but a Car Honk's and a Moped Meep Meeps!
class Vehicle {
public void beep() {
System.out.println("Beep!");
}
}
class Car extends Vehicle {
#Override
public void beep() {
System.out.println("Honk!");
}
}
class Moped extends Vehicle {
#Override
public void beep() {
System.out.println("Meep Meep!");
}
}
That's called Polymorphism. Have a look at the link.
A simple example is the Java Collections framework. You have an interface List when you are using it. You don't want to know how add or size is working. It should just work.
But when you are on the implementation side there are differences for ArrayList or a LinkedList in the way how they manage the items, that where stored in there.
When you look at already implemented methods in super classes, you might need one method to have a different behavior. Now pretend you have some method calls in your current code. If you don't want them to change, so you just override that method and the client does not have to change his code in order to use your functionality.
Overriding is a feature that is available while using Inheritance.
It is used when a class that extends from another class wants to use most of the feature of the parent class and wants to implement specific functionality in certain cases.
class Animal{
public void move(){
System.out.println("Animals can move");
}
}
class Dog extends Animal{
public void move(){
System.out.println("Dogs can walk and run");
}
}
class Cat extends Animal{
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal reference and object
Animal b = new Dog(); // Animal reference but Dog object
Animal c = new Cat(); // Animal reference but Cat object
a.move();// runs the method in Animal class
b.move();//Runs the method in Dog class
c.move();//Runs the method super method
}
}
So by using a parent class you dont need to define move() in each of class that extends animal you can override it wherever necessary.
We have a Class(say Animal), and we have an Interface(say Behave). Both Animal as well as Behave have a method with the same signature(say public void eat()).
When we try to write the body for the method eat() in a Class(say Dog) which extends Animal and implements Behave, which eat() method is actually referred to? The one in Animal or Behave. In whichever case that happens, why does it happen that way?
Edit:
I tried this scenario on Eclipse before posting this question.
An interesting part here is, even though I am implementing Behave, if I dont create an eat() method(i.e. if I dont implement Behave's inherited abstract method) inside Dog, there is no error, since I am already extending from Animal which has an eat() method.
which eat() method is actually referred to? BOTH.
Try this: if you don't override the method at all, when you call with the interface, you will get the one from the parent.
Behave b = new Dog();
System.out.println(b.eat());//this will call eat from Animal if Dog did not override.
If you override, you always get the one from the child:
Behavior b = new Dog();
Animal a = new Dog();
a.eat() and b.eat() will both refer to the eat inside of Dog class.
USE THESE CLASSES:
public class BClass {
public int add(int a, int b){
return a*b;
}
}
public interface BInterface {
public int add(int a, int b);
}
public class BChild extends BClass implements BInterface{
public static void main(String... args){
BInterface bi = new BChild();
BClass bc = new BChild();
System.out.println(bi.add(3, 5));
System.out.println(bi.add(3, 5));
}
#Override
public int add(int a, int b){
return a+b;
}
}
interface can contain only body definition of method , once you implements, it must have implementation of all defined methods. In you example
class Dog extends Animal implements Behave
{
#Override
public void eat() {...}
}
abstract class Animal{
public abstract void eat();
}
interface Behave{
void eat();
}
Here it need a body of abstract method where as it is in Main method. In other way
class DOG extends Animal implements Behave{
...
}
class Animal{
public void eat(){
...
}
}
interface Behave{
void eat();
}
Here Dog class having eat method body in its super class Animal. So it wount ask to implement body again in Animal as it is already implemented.
An interface simply defines a method that a class must provide. If we have
public class Animal{
public void eat(){
System.out.println("Om nom nom");
}
}
public class Dog extends Animal{
}
Dog now provides the eat method and can so implement Behave.
There is only one eat() method, because the designers of the language decided that the simplicity of having the method signature consist of its name and argument types was more useful than having the complexity of being able to specify that you are providing an implementation of an interface.
In Java, if the two have different semantics, provide a method which returns a Behave instance which does something else:
class Animal {
public void eat () { }
}
interface Behave {
void eat ();
}
class Dog extends Animal {
public void eat () {
// override of Animal.eat()
}
public Behave getBehave() {
return new Behave {
public void eat() {
BehaveEat();
}
};
}
private void BehaveEat() {
// effectively the implementation Behave.eat()
}
}
In other languages, you can explicitly state that a method implements a method from an interface.