Java - 确定子类的更智能方法?

时间:2017-01-06 21:26:42

标签: java class inheritance

我需要一种智能的方法来区分多个对象,来自同一父类的所有子类。考虑这个父类和两个子类:

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.");
    }
}

(另外假设我有一个 很多 更多的水果子课程,这里没有显示。)现在我想将所有水果存放在水果车中:

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();
        }
    }
}

cartInventory()方法查找Apple对象,然后运行特定于Apples的方法。我将回到那个&#34;我如何识别Apple&#34;在第二部分,但首先,我从main()运行整个程序:

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

BTW,输出是:

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.

所以整个过程都很好......但我对检查Fruit对象并确定Fruit是否是Apple的机制感到困扰。我这样做的方式非常笨拙:

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

基本上,我调用getName()并进行字符串比较以查看cart[i]是否为Apple。难道没有更聪明的方法吗?我一直认为必须以某种方式使用cart[i].getClass() ...但我不会看到更聪明的方式。是不是有某种方式可以利用Fruit&amp; amp; amp; amp ;;苹果?

有什么建议吗?谢谢!

12 个答案:

答案 0 :(得分:5)

您可以将方法checkPrice放在Fruit课程中,最终每个孩子都可以覆盖它。

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(){}

}

现在cartInventory看起来像这样:

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();
    }
}

答案 1 :(得分:3)

我想你可以查看instanceOf。 例如:

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

然而在实践中,它不应该被用作反对OOPS概念。

答案 2 :(得分:3)

我会避免使用反射并坚持使用多态。你甚至不需要instanceof。只需在Fruit类上声明一个只为Apple重写的空方法。此外,您可能需要考虑将价格提高到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.");
    }
}

现在你可以这样做:

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

答案 3 :(得分:3)

你必须决定所谓的苹果特定方法(在这种情况下checkPrice())是否真的特定于Apple。或者它实际上通常适用于所有水果。

通常适用的方法应在基类中声明

假设答案是肯定的(在这种情况下似乎确实如此),那么您应该在基类中声明该方法。在这种情况下,您可以遍历所有不同类型的水果,并且所有水果都会接受方法checkPrice(),因此您甚至不需要为苹果制作特殊情况。

可以在界面中声明一般不适用的方法

如果答案是否定的,该怎么办?我们假设我们需要另一种称为getJuicePrice()的方法,我们进一步假设只有一些水果可以制成果汁(苹果汁,橙汁),但其他水果不能(菠萝?榴莲?)。在这种情况下,一个简单的解决方案是声明一个接口,只有该方法适合的果实才能实现该接口。因此,我们假设此界面为JuiceBehavior

package fruitcart;

import java.math.BigDecimal;

public interface JuiceBehavior {
    BigDecimal getJuicePrice();
}

果汁行为适用的所有水果(Apple为是,Durian为否)将实施界面:

package fruitcart;

import java.math.BigDecimal;

public class Apple implements JuiceBehavior {

    @Override
    public BigDecimal getJuicePrice() {
        // FIXME implement this
        return null;
    }

}

然后在你的循环中,你检查的是fruitinstanceof界面:

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");

}

此解决方案适用于简单的情况,但在更复杂的情况下,您可能会注意到您开始为不同类型的水果复制getJuicePrice()的许多实现代码。这导致了下一个主题

设计模式:策略

你可以 我想开始考虑名为策略设计模式,其中 进一步封装JuiceBehavior并使其成为代表不同果汁行为的类家族。它还允许您设置不同类型的水果,以采用JuiceBehavior的不同实现。我不打算在这里详细介绍。但是你可以在一些关于设计模式的书上阅读。如

  1. Design Patterns: Elements of Reusable Object-Oriented Software
  2. Head First Design Patterns: A Brain-Friendly Guide

答案 4 :(得分:2)

正如您所看到的,有多种方法可以解决这个问题。要选择哪一个是正确的,你需要确切地知道你想要什么。

如果你只关心苹果,并且预计不会将这个功能扩展到苹果之外,那么试试这个:

if (cart[i] instanceof Apple)

instanceof关键字专门用于此目的,以检查对象是否是特定类型的实例。

如果您现在关心苹果,但可能希望将来为其他水果实现此功能,那么您应该将price字段移到Fruit类中,然后添加{{1}没有做任何事情的水果类的方法。这样就不会强迫你在每个子类中实现它,而只是在你想要的那些中覆盖它。

如果你想要所有水果的这个功能,你可以在checkPrice()类中使checkPrice()方法摘要,但更可能的方法是这么简单,你最好提供一个默认的实现,你可以在必要时覆盖它。

答案 5 :(得分:2)

可能有不同的事情:

例如,使类Fruit abstract 并添加一个抽象方法 checkPrice(),所有子类都需要实现它。

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

    public abstract void checkPrice();
}

最后一行使编译器强制您在所有子类(在您的情况下为Apple和Orange)上实现checkPrice();

然后您的方法可能如下所示:

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

您可以更多地改进这一点,并避免使用例如(Fruit)使用泛型,但我认为它证明了这一点。 这种方法使用Abstract类的概念。你可以继续进行混合界面,完成与其他答案相似的内容。

如果您不希望被强制在每个子类中实现它,那么您可以删除父Fruit类中的abstract关键字并在那里添加默认实现。

作为建议我建议熟悉java接口与抽象类的概念。两者都可以混合起来做有用的事情。

答案 6 :(得分:2)

执行getClass().getName()getClass().equals(Apple.class)确实不是一个非常易于维护的解决方案。

您可以在基类中添加public void checkPrice()Fruit并提供一个空实现:

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

支票允许检查。如果有时你在其他情况下检查一些你可能无法检查的东西 这样做允许对此层次结构的所有类使用相同的层次结构和通用方法:checkPrice()

在您的上下文中,我认为这个解决方案更好。

如果你真的想让checkPrice()仅在Apple中可用,你就没有选择,你应该能够区分必须执行检查的实例,以及没有检查的实例。
如果你有许多需要checkPrice()方法的子类,你不应该使用instanceof或getClass()比较来识别每个类,因为它不可维护。更好的解决方案是引入一个接口来解决这个问题:

public interface IsPriceChecking{
   void checkPrice();
}

所有需要这个的类都应该实现它。 例如:

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.");
  }
}

你可以从客户端那里做到:

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();
    }
}

答案 7 :(得分:2)

因为我不太喜欢继承,所以我以其他方式实现代码

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();
            }
        }

    }
}

答案 8 :(得分:1)

例如,您可以使用instanceof运算符。

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

您还可以在fruit上实现isApple函数,您可以在Apple类中覆盖它。根据定义,它将返回false,但在Apple中,它会返回true。我个人比较喜欢上一个。

但还有第三个,可能会更好。设checkprice()为父类的方法。在每个子类中,您都可以实现它。在苹果公司,它会做它现在做的事情,而在别人看来可能是其他东西,也许什么都没有由你决定。

在所有三个中,我更喜欢这个。

答案 9 :(得分:1)

你可以让超类有一个抽象方法public string GetType()并让每个子类实现它,返回类型的名称。然后,使用switch statement

答案 10 :(得分:1)

instanceof关键字也会为superclasses返回true。

如果要查看对象是否是类的直接实例,可以比较该类。您可以通过getClass()获取实例的类对象。您可以通过ClassName.class静态访问特定的课程。

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

如果a是X的实例,则条件为真,但如果a是X的子类的实例,则条件为真。

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

如果a是X的实例,或者如果a是X的子类的实例,则条件为真。

答案 11 :(得分:1)

我建议你制作一个界面。

public interface PriceCheckable {
    public void checkPrice()
}

然后让你的苹果实现它

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.");
    }
}

然后在循环中检查实例,强制转换和访问。

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();
    }
}

然后,您可以将价格检查添加到您想要的任何水果,产品或物品上。

阅读java接口。

要点是: 接口是您与代码之间的契约。

如果对象实现了接口,则该对象至少需要具有这些对象。如果代码不存在,代码将无法编译。

所以如果有界面

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

任何具有此方法的对象都需要在他们认为合适的情况下实现上述三种方法。

<强>旁注

  

通常,方法后面跟着预期用途的评论   可选的额外限制和期望,但更多的想法   那些作为建议。

     

enter image description here

     

虽然,如果您不保留这些建议,那么期望的代码   某些限制会破裂。

任何具有接口的类都可以强制转换为接口类型的对象。

Alphabet alphabetInstance = (Alphabet)myobject;

您无法访问myobject对象alphabetInstance的常规方法。 alphabetInstance实例可用的唯一方法是界面中概述的方法。

它允许您访问该方法,但限制对其他方法的访问。

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);
}

要么像上面那样做,如果一个对象属于某个接口就有一个明确的测试,并且在if语句中处理接口代码,或者我喜欢的,只为那个部分做一个特殊的方法。

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());
}

为它制作一个额外的方法似乎是不必要的工作,但也可能是需要更多使用的情况,它可以节省你复制和粘贴代码的时间。