用Java中的多态替换条件

时间:2019-08-22 15:26:36

标签: java inheritance polymorphism

我最近开始独自学习和学习Java,但在理解如何使用多态性进行更好的编码实践时遇到了麻烦。如何使用多态重构来消除以下代码中的if和else条件?

我有一个父类的水果,有子类的奇异果和苹果以及其他一堆水果。 例如:苹果甜,奇异果变酸。

我有一个人类班,一个孩子班,男孩和女孩。 男孩喜欢甜味,女孩喜欢酸味。 我创建了一个水果对象数组,并使用if语句根据口味来检查谁喜欢每个水果。

Fruit[] fruit = new Fruit[2];
fruit[0] = new Apple ();
fruit[1] = new Kiwi ();
Boy boy1 = new Boy ();
String boyTaste = boy1.taste;
for (int i = 0; i < fruit.length; i++){
    if (fruit[i].flavor.equals (boyTaste)){
        System.out.println ("Boy likes " + fruit[i].name + " because " + fruit[i].taste ());
    } else {
        System.out.println ("Girl likes " + fruit[i].name +  " because " + fruit[i].taste ());
    }
}

如何通过用多态替换条件来改善以下代码?

5 个答案:

答案 0 :(得分:0)

首先,从不,请使用==比较String。始终使用.equals(),例如str.equals("hello")

尚不清楚使用特定的多态性/继承性可以改善什么。可以提出一个论点,应将Boy boy1 = new Boy();替换为Human boy1 = new Boy();,但是在这种情况下,由于它是仅使用一次并在方法中实例化的局部变量,因此不会差异很大。

您可能想做的就是在Human上创建名为likesFruit()getMessage()的方法,如下所示:

public abstract class Human {

    ...

    public boolean likesFruit(Fruit f) {
        return f.getFlavor().equals(getTaste());
    }

    public abstract String getMessage();

    ...
}

getMessage()Boy中的定义为:

public String getMessage() {
    return "Boy likes %s because ...";  // truncated for brevity
}

Girl的类似实现。

然后在您的代码中,您可以调用:

Human human = new Boy();
Fruit[] fruits = ...

for (Fruit fruit : fruits) {
    if (human.likesFruit(fruit)) System.out.println(String.format(human.getMessage(), fruit.getName(), fruit.getFlavor()));
}

一些注意事项:

  • 我用getters / setters替换了字段引用(例如fruit.flavor变成fruit.getFlavor()。这是一个好习惯,因为它对调用者隐藏了必要的状态,并允许您在以下情况执行逻辑:吸气剂被称为。

  • HumanabstractHuman既不是Boy也不是Girl(或者Man / Woman等,除了政治之外)是不可能的,所以应该实例化纯Human是非法的。

  • 类似地,getMessage()是抽象的,因为实现是基于子类更改的

答案 1 :(得分:0)

在修复比较之后,为了引入多态性,您需要将“条件”从main()方法移至类定义中。在某种程度上,尽管如此,您仍然无法避免使用if / else。但是您的“多态”主方法中的for循环可能看起来像这样:

Human person = new Boy(FlavorPreferences.SWEET)
for (int i = 0; i < fruit.length; i++) { 
    System.out.println(person.likes(fruit[i]);
}

也-仅仅因为男孩不喜欢给定的“水果”,并不一定意味着女孩喜欢相同的水果。也许有些男孩和女孩在水果味上口味相同。

答案 2 :(得分:0)

我将以以下方式更改代码:

  • 如果可能的话,将枚举用于调味品-将帮助您避免很多编码错误。

  • 使用更现代的方法替换find方法,也许使用流API(如果Java版本允许)

  • 将代码拆分为功能全面的方法,并将其分布在各个类中 以一种有意义的方式

到目前为止,这是我的写法:

我将创建一个这样的水果类,并将风味枚举添加为子类(因为风味是水果的属性)

public class Fruit {
private FLAVOR flavor;

public Fruit( FLAVOR flavor ) {
    this.flavor = flavor;
}

public FLAVOR getFlavor() {
    return this.flavor;
}

public static enum FLAVOR {
    SWEET, 
    SOUR;
}

}

人类阶级看起来像这样

public class Human {
private FLAVOR flavor;
public Human( FLAVOR flavor ) {
    this.flavor = flavor;
}

public List<Fruit> findLikedFruit( List<Fruit> availableFruits ) {
    List<Fruit> likes = availableFruits.stream().filter( candidate -> Human.this.flavor == candidate.getFlavor() ).collect( Collectors.toList() );
    return likes;
}

}

它基本上包含喜欢的风味,以及一种消耗可用水果列表并返回仅包含喜欢的水果的列表的方法

最终,水果和人类都将像这样延伸(仅在水果上为例,但原理适用)

public static class Apple extends Fruit {
    public Apple() {
        super(FLAVOR.SOUR);
    }
}

主类如下所示:

public static void main( String[] args ) {
    List<Fruit> fruits = Arrays.asList( new Fruit[] {new Apple(), new Pear(), new Strawberry()});
    List<Fruit> boyLikes = new Boy().findLikedFruit(  fruits );
    //if you wanna output it, you could do like so
    boyLikes.forEach(fruit -> System.out.println( fruit.getFlavor().name() ) );
}

这使代码方式更安全,将来可以扩展得多,并且符合现代Java编码惯例。

希望有帮助

欢呼

答案 3 :(得分:0)

您遇到麻烦的地方是,您试图将多态性用于简单的属性-水果的味道。多态性是针对不同的行为,而不仅仅是针对不同的值。我试图举一个带有水果的例子,但是失败了。

请考虑银行帐户。假设有一个定义该接口的银行帐户的抽象概念,包括一个withdrawMoney(double amount)方法。

interface Account {  
  boolean withdrawMoney(double amount);
}

class SimpleAccount implements Account {
  double cash;   // Assume initialized in constructor, not shown.
  boolean withdrawMoney(double amount) {
    if (amount <= cash) {
      cash -= amount;
      return true;
    } else {
      return false;
    }
  }
}
class OverdraftProtectedAccount implements Account {
  double cash;
  Account overdraftAccount;  // Assume both initialized in constructor
  boolean withdrawMoney(double amount) {
    if (amount <= cash) {
       cash -= amount;
       return true;
    } else if (overdraftAccount.withdrawMoney(amount - cash)) {
       cash = 0;
       return true;
    } else {
       return false;
    }
  }
}

当然,此示例在计时方面存在很多问题,并且仅返回布尔值,但重点是要说明多态性是关于行为而非数据的想法。

答案 4 :(得分:-1)

让我们写一个函数来检查某人是否喜欢水果

public boolean likesThisFruit(Human human, Fruit fruit) {}

您要检查human是否喜欢fruit

if ( (human instanceof Boy && fruit.taste.equals("sweet"))
    || (human instanceof Girl && fruit.taste.equals("sour"))
) {
    return true;
} else {
    return false;
}

等效于

return (human instanceof Boy && fruit.taste.equals("sweet")) 
    || (human instanceof Girl && fruit.taste.equals("sour"));

请注意,您可以使用instanceof检查对象是否为给定类型的实例。
您的最终功能

public boolean likesThisFruit(Human human, Fruit fruit) {
    return (human instanceof Boy && fruit.taste.equals("sweet")) 
        || (human instanceof Girl && fruit.taste.equals("sour"));
}

我建议您使用Interfaces:酸甜的。并在函数中使用instanceof

public boolean likesThisFruit(Human human, Fruit fruit) {
    return (human instanceof Boy && fruit instanceof Sweet) 
        || (human instanceof Girl && fruit instanceof Sour);
}

public class Apple extends Fruit implements Sweet {}

编辑以匹配操作请求

interface Taste {}
interface Sour extends Taste {}
interface Sweet extends Taste {}
public abstract class Human {
    protected final Class taste;
    Human(Class taste) {
        this.taste = taste;
    }
    public boolean likes(Fruit fruit) {
        return fruit instanceof taste;
    } 
}

public class Boy extends Human {
    Boy(Class taste) {
        super(taste);
    }
}

我在这里假设Fruit实现了Sour或Sweet接口

Boy boy = new Boy(Sweet.class);
boy.likes(new Apple()); //true
boy.likes(new Kiwi()); //false if Kiwi implements Sour

分配味道时,应通过设置员检查通过的类是否为味道(带有instanceof)。