A smarter way to define a child class?

I need an intelligent way to distinguish between multiple objects, all child classes from the same parent class. Consider this parent class and two child classes:

public class Fruit { String name; public Fruit(String a){ this.name = a; } } public class Orange extends Fruit { public Orange(String a){ super(a); } } public class Apple extends Fruit { int price; public Apple(String a, int b){ super(a); this.price = b; } public void checkPrice(){ if(this.price>2) System.out.print(" -- This apple is too expensive!"); else System.out.print(" -- This apple is cheap."); } } 

(Also suppose I have a lot more classes for Fruit children, not shown here.) Now I want to store all the fruits in a fruit basket:

 public class FruitCart { Fruit[] cart; public FruitCart(){ cart = new Fruit[4]; cart[0] = new Apple("Apple #1", 1); cart[1] = new Orange("Orange #1"); cart[2] = new Apple("Apple #2", 3); cart[3] = new Apple("Apple #3", 2); } public void cartInventory(){ for(int i=0; i<cart.length; i++){ System.out.print("Item "+i+") "+cart[i].getClass()); if(cart[i].getClass().getName().equals("area.Apple")){ Apple tmp = (Apple)cart[i]; tmp.checkPrice(); } System.out.println(); } } } 

The cartInventory() method cartInventory() for Apple objects and then runs the Apple-specific method. I will return to the fact that “how do I recognize Apple” in a second, but first I ran the entire program from main() :

 public class MainEvent { public static void main(String[] args){ FruitCart cart = new FruitCart(); cart.cartInventory(); System.out.println("END OF PROGRAM."); } } 

BTW, output:

 Item 0) class area.Apple -- This apple is cheap. Item 1) class area.Orange Item 2) class area.Apple -- This apple is too expensive! Item 3) class area.Apple -- This apple is cheap. END OF PROGRAM. 

So, everything works fine ... but I'm worried about a mechanism that checks Fruit objects and determines if Fruit is Apple. The way I do this is pretty awkward:

 if(cart[i].getClass().getName().equals("area.Apple")) 

Basically, I call getName() and compare String to see if cart[i] Apple or not. Is there no smarter way to do this? I keep thinking that there should be a way to use cart[i].getClass() some way ... but I don't see a more reasonable way. Is there any way to take advantage of the parent-child relationship between Fruit and Apple?

Any suggestions? Thanks!

+5
source share
12 answers

Doing getClass().getName() or getClass().equals(Apple.class) really not a very convenient solution.

You can add the public void checkPrice() to the base class: Fruit and provide an empty implementation:

  public class Fruit { ... public void checkPrice(){} } 

Verification allows you to verify. If sometimes you check something in other cases, during which you will have nothing to check. This allows you to use the same hierarchy with a common method for all classes in this hierarchy: checkPrice() .

In your context, this solution is better than I think.


If you really want to have checkPrice () available only in Apple, you have no choice, you should be able to distinguish between instances that must perform a check from which there is no check to execute.
And if you have many classes for children that must have the checkPrice () method, you should not use instanceof or getClass () to identify each class, because it is not supported. A better solution would be to introduce an interface to solve this feature:

 public interface IsPriceChecking{ void checkPrice(); } 

All classes that must have this must implement it. For instance:

 public class Apple extends Fruit implements IsPriceChecking{ public void checkPrice(){ if(this.price>2) System.out.print(" -- This apple is too expensive!"); else System.out.print(" -- This apple is cheap."); } } 

and you can do it from the client side:

 public void cartInventory(){ for(int i=0; i<cart.length; i++){ if(cart[i] instanceof isPriceChecking){ IsPriceChecking tmp = (IsPriceChecking)cart[i]; tmp.checkPrice(); } System.out.println(); } } 
+2
source

You can put the checkPrice method in your Fruit class, and ultimately every child can override it.

 public class Fruit { String name; public Fruit(String a){ this.name = a; } // just a default implementation that does nothing // (or does whatever you want) public void checkPrice(){} } 

Now cartInventory will look like this:

 public void cartInventory(){ for(int i=0; i<cart.length; i++){ System.out.print("Item "+i+") "+cart[i].getClass()); cart[i].checkPrice(); System.out.println(); } } 
+5
source

I assume you can check with instanceOf. For instance:

 if(cart[i] instanceOf Apple){ // do something } else if(cart[i] instanceOf Orange){ //do something } 

However, in practice, it should not be used as its against the concept of OOPS.

+3
source

I would avoid using reflection and stick to using polymorphism. You don't even need instanceof . Just declare an empty method in the Fruit class, which is only overridden for Apple. In addition, you might consider raising the price to Fruit .

 public class Fruit { String name; public Fruit(String a){ this.name = a; } public void checkPrice() { //default implementation is do nothing } } public class Orange extends Fruit { public Orange(String a){ super(a); } } public class Apple extends Fruit { int price; public Apple(String a, int b){ super(a); this.price = b; } @Override public void checkPrice(){ if(this.price>2) System.out.print(" -- This apple is too expensive!"); else System.out.print(" -- This apple is cheap."); } } 

Now you can do:

 public void cartInventory(){ for(int i=0; i<cart.length; i++){ cart[i].checkPrice(); } } 
+3
source

You must decide whether the Apple-specific method (in this case checkPrice() ) is Apple-specific. Or is it generally applicable to all fruits.

A method that is commonly applicable must be declared in the base class

Assuming the answer is yes (in this case, it seems yes), then you should declare the method in the base class. In this case, you can checkPrice() over all types of fruits, and all of them will accept the checkPrice() method, so you don’t even need to make a special case for apples.

A method that is usually not applicable can be declared in the interface.

What if the answer is no? Suppose we need another method called getJuicePrice() , and we also assume that only some fruits can be made into juice (apple juice, orange juice), but others cannot (pineapple? Durian?). In this case, a simple solution is to declare an interface, and only the fruits for which this method is suitable will implement the interface. So let's say this JuiceBehavior interface

 package fruitcart; import java.math.BigDecimal; public interface JuiceBehavior { BigDecimal getJuicePrice(); } 

And all fruits for which juice behavior is applicable (yes for Apple , no for Durian ) will implement the interface:

 package fruitcart; import java.math.BigDecimal; public class Apple implements JuiceBehavior { @Override public BigDecimal getJuicePrice() { // FIXME implement this return null; } } 

And then in your loop, what you check to see if fruit instanceof interface:

 if (fruit instanceof JuiceBehavior) { System.out.format("can be made into juice " + "with price $ %.2f%n", fruit.getJuicePrice()); } else { System.out.format("cannot be made into juice %n"); } 

This solution will work for simple cases, but in more complex cases, you may notice that you are starting to duplicate a lot of implementation code for getJuicePrice() for different types of fruits. This leads to the next topic.

Design Template: Strategy

You may want to start thinking about a Design Template called Strategy , which further encapsulates JuiceBehavior and turns it into a family of classes representing various juice behaviors. It also allows you to set different types of fruits to perform different JuiceBehavior implementations. I will not go into details here. But you can read about it in some books on Design Patterns. For instance,

+3
source

As you can see, there are several ways to handle this. To choose the right one, you need to know exactly what you want.

If you only ever care about apples and don't plan to extend this functionality outside of Apple, try this:

if (cart[i] instanceof Apple)

The instanceof designed specifically for this purpose to check if an object is an instance of a particular type.

If you are interested in apples now, but you might want to implement this for other fruits in the future, then you should move the price field to the Fruit class and add the checkPrice() method to the fruit class that does nothing. Thus, you should not implement it in each subclass, but only redefine it in those which you want.

If you want this functionality for all fruits, you can abstract the checkPrice() method in the Fruit class, but most likely with this method you will be better off providing a default implementation and you can override it if necessary.

+2
source

Different things are possible:

For example, make the class Fruit abstract and add the abstract method checkPrice (), which all subclasses need to implement.

 public abstract class Fruit { String name; public Fruit(String a){ this.name = a; } public abstract void checkPrice(); } 

The last line will force the compiler to force you to implement checkPrice(); in all subclasses (Apple and Orange in your case).

Then your method might look like this:

 public void cartInventory(){ for(int i=0; i<cart.length; i++){ Fruit tmp = (Fruit)cart[i]; tmp.checkPrice(); } } 

You can improve this a lot more and avoid casting (Fruit) , for example. using Generics, but I think it demonstrates the point. This approach uses the concept of abstract classes. You can go ahead and bring the interface to a mix that does something similar, as other answers show.

If you do not want to be forced to implement it in each subclass, you can remove the abstract keyword in the parent class Fruit and add the default implementation there.

As a recommendation, I suggest that you familiarize yourself with the concept of java interfaces against an abstract class. Both can be mixed to do useful things.

+2
source

Because I'm not a lot of fans of inheritance, so I implement the code in a different way

 public interface Fruit { } public interface Price { public void checkPrice(); } public class Orange implements Fruit{ } public class Apple implements Fruit, Price{ @Override public void checkPrice() { System.out.println("Apple"); } } public class Cart { Fruit[] cart; public Cart(){ cart = new Fruit[4]; cart[0] = new Apple(); cart[1] = new Orange(); cart[2] = new Apple(); cart[3] = new Apple(); } public void cartInventory() { for (Fruit fruit : cart) { if (fruit.getClass().isAssignableFrom(Apple.class)) { ((Apple)fruit).checkPrice(); } } } } 
+2
source

For example, you can use the instanceof operator.

 if(cart[i] instanceof Apple){ Apple tmp = (Apple)cart[i]; tmp.checkPrice(); } 

You can also implement the isApple function for fruits, which you would override in the Apple class. By definition, it will return false , but at Apple it will return true . I personally prefer the previous one.

But there is a third that could be better. Let checkprice() be the method of the parent class. And in each child class you implement it. In Apple, this will do what he is doing now, while in others - something else, maybe nothing. To you.

Among all three, I prefer this one.

+1
source

You could have a superclass with the abstract public string GetType() method public string GetType() and each subclass implement it by returning the type name. Then use the operator .

+1
source

The instanceof will return true for superclasses .

If you want to see if an object is a direct instance of a class, you can compare the class. You can get an instance class object via getClass() . And you can statically access a specific class through ClassName.class .

 if (a.getClass() == X.class) { // do something } 

Here the condition is true if a is an instance of X, but not if a is an instance of a subclass of X.

  if (a instanceof X) { // do something } 

Here the condition is true if a is an instance of X or if a is an instance of a subclass of X.

+1
source

I would suggest you create an interface.

 public interface PriceCheckable { public void checkPrice() } 

Then your apple sells it

 public class Apple extends Fruit implements PriceCheckable { int price; public Apple(String a, int b){ super(a); this.price = b; } public void checkPrice(){ if(this.price>2) System.out.print(" -- This apple is too expensive!"); else System.out.print(" -- This apple is cheap."); } } 

Then in your loop you check for instance, listing, and access.

 public void cartInventory(){ for(int i=0; i<cart.length; i++){ System.out.print("Item "+i+") "+cart[i].getClass()); if(cart[i] instanceof PriceCheckable){ PriceCheckable tmp = (PriceCheckable)cart[i]; tmp.checkPrice(); } System.out.println(); } } 

You can then add a pricecheckable to any fruit, product, or thing you want.

Reading on java interfaces.

The bottom line is this: An interface is a contract between you and the code.

If an object implements an interface, this object must have at least those objects. Code will not compile if they are missing.

so if there is an interface

 public interface Alphabet { public void A(); public int B(int b); public int C(); } 

Any object that has this method is required to implement the three methods above as they see fit.

Sidenote

Usually the methods are followed by comments for the intended use, with additional additional restrictions and expectations, but one camera thinks more about those as suggestions.

enter image description here

Although, if you do not save these sentences, the code that expects certain restrictions will break.

Any class that has an interface can be passed to an object of type interface.

 Alphabet alphabetInstance = (Alphabet)myobject; 

You cannot access the normal methods of the myobject alphabetInstance object. The only methods available for the alphabetInstance instance are those described in the interface.

It allows you to access a method, but restricts access to other methods.

 public void foo(MyObject myobject) { Alphabet alphabet = null; int worth = 10; if(MyObject instanceof Alphabet) { alphabet = (Alphabet)myobject; worth += alphabet.B(alphabet.C()); } myobject.handleWorth(worth); } 

Either you do this, as above, have an explicit test if the object has a specific interface, and process the interface code in the if statement or, what I prefer, make a special method for this part only.

 public void foo(MyObject myobject) { Alphabet alphabet = null; int worth = 10; if(myobject instanceof Alphabet) { worth += this.getAlphabetWorth(myobject); } myobject.handleWorth(worth); } public int getAlphabetWorth(Alphabet alphabet) { return alphabet.B(alphabet.C()); } 

It may seem like it’s not an accidental job to make an additional method for it, but it may be the cases when you need to use more, and this saves you from copying and pasting code.

+1
source

Source: https://habr.com/ru/post/1262447/


All Articles