我最近开始独自学习和学习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 ());
}
}
如何通过用多态替换条件来改善以下代码?
答案 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()
。这是一个好习惯,因为它对调用者隐藏了必要的状态,并允许您在以下情况执行逻辑:吸气剂被称为。
Human
是abstract
。 Human
既不是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)。