最初,我创建了一个接口,其中包含将在两个类之间共享的所有方法,但是,我意识到我希望两个类都具有相同的方法,但是它们的行为将有所不同。
它们的返回类型相同,但参数不同。我不知道该怎么执行,或者即使我确实想知道如何实现,我也不知道这是否是应对情况的正确方法
基本上,我来这里是为了寻找我想要达到的目标的正确体系结构方法,我不知道那会是什么。我认为我有4个问题来确定代码体系结构:
public interface Character {
public void setAttack();
}
/*the setAttack method here will be set by the programmer. The 3 values
passed by the programmer are then stored into an array*/
public class Player implements Character {
public void setAttack(int x, int y, int z) {
attackArray[0] = x;
attackArray[1] = y;
attackArray[2] = z;
}
}
/*the setAttack will still serve the same purpose as the setAttack in the
player class, however, the values will be auto generated randomly once the
setAttack function is called for the NPC instance.*/
/*Another thought I had is passing the function that auto generates the 3
integer values (numGen()) as a parameter 3 times, however, I'm not sure if
this is possible. Just a thought*/
public class NPC implements Character {
public void setAttack(){
for(int i = 0; i < attackArray.length; i++)
{
attackArray[i] = numGen();
}
}
}
答案 0 :(得分:6)
有一个概念上的误解:拥有 same 方法比实现具有相同 name 名称的方法更有意义。
当您在Java中使用多态和接口时,您将表达意图。如:实现某些接口的类必须提供相应的“功能”。换句话说:这描述了某种行为。
事实是:当接口具有foo()
,并且不同的类可能需要foo(X)
和foo(Y)
时,真正的问题是:这些方法是否有更多的共同点?名字?!
如果是这样,一个可能的解决方案将是另一层抽象。就您而言,例如:
public interface AttackParameters {
...
public interface Character {
public void setAttack(AttackParameters parms);
或类似的东西。想法是用通用解决方案再次替换“特定”细节。
或者,您可以在Map<String, Object>
方法中使用setAttack()
自变量。换句话说:攻击参数由字符串标识。
这很好并且很动态,但是也避开了编译时间安全性。映射方法的一种更好的版本将不使用字符串作为键,而是使用某些枚举类。
答案 1 :(得分:2)
这将有助于理解方法是由其 signature (其名称和参数)定义的。您只能覆盖具有相同签名的方法。返回类型无关紧要,因为它总是可以忽略的。
如果类型中有2种名称相同但名称不同但方法不同的方法,则称它们被重载了。
>当可以用不同的方式表示输入时,通常使用重载的方法,但是该方法的行为仍然相同。这是正确使用重载的常见示例:
double distanceFromOrigin(double x, double y) {
return Math.hypot(x, y);
}
double distanceFromOrigin(Point point) {
return distanceFromOrigin(point.getX(), point.getY());
}
另一种用法是为方便的方法使用较少的参数,是完整方法的快捷方式:
void setLocation(int x, int y) {
this.x = x;
this.y = y;
}
/**
* A convenience method for setting the location to the origin.
*
* @implSpec This call is equivalent to {@code setLocation(0, 0)}.
*/
void setLocation() {
setLocation(0, 0);
}
但是,如果方法不做相同的事情,只是给它们相同的名称通常是不明智的,因为这样做并不能使意图明确。
在您的情况下,Player
和NPC
都有一个int[3]
,由方法填充。我将使用一个abstract
类来保存数组。现在,您的选择是在每种级别使用哪种方法。如果两个类都可以使用它们,请在超类级别使用它们:
public abstract class Character {
int[] attackArray = new int[3];
public void setAttack(int x, int y, int z) {
attackArray[0] = x;
attackArray[1] = y;
attackArray[2] = z;
}
/**
* Populates the attack array with random numbers.
*/
public void setAttack() {
setAttack(numGen(), numGen(), numGen());
}
}
在这种情况下,您有重载的方法,它们都被Player
和NPC
继承。如果每个类只能使用自己的方法,则将它们放在子类级别:
public class Player extends Character {
public void setAttack(int x, int y, int z) {
attackArray[0] = x;
attackArray[1] = y;
attackArray[2] = z;
}
}
public class NPC extends Character {
public void setAttack() {
for(int i = 0; i < attackArray.length; i++) {
attackArray[i] = numGen();
}
}
}
也许您想让两个类都可以使用(int x, int y, int z)
方法(将其放入超类中),而无参数的方法仅对NPC可用(将其放入其子类中)。
您需要问自己的是如何使用这些方法。基于此,您将知道将它们放在哪里。
答案 2 :(得分:1)
我知道您已经接受了答案,但仍然想为您提供我的解决方案。如果您也要通过它,我将不胜感激。
在您的情况下,我们可以使用varargs
。它减少了很多样板代码。现在,其他答案已经表明,您将需要为setAttack()
(取决于参数的数量)或多个接口创建不同的方法。但是现在假设您有10个不同的排列和参数组合,是否要创建10个单独的接口或10个不同的方法签名?也许吧,也许不是。
使用varargs
可以达到以下目的。声明您的抽象类:
public abstract class Character {
int[] attackArray = new int[3];
public abstract void setAttack(int... values); //using varargs
}
现在每个类都可以扩展它并根据需要覆盖它。
// Player
public class Player extends Character {
@Override
public void setAttack(int... values) {
attackArray[0] = values[0];
attackArray[1] = values[1];
attackArray[2] = values[2];
}
}
// NPC
public class NPC extends Character {
@Override
public void setAttack(int... values) {
for(int i = 0; i < attackArray.length; i++) {
attackArray[i] = numGen();
}
}
}
因此,使用这种方法,您只有一个抽象方法setAttack()
及其不同的实现,并且全部都只使用一个抽象类。