【问题标题】:Proper use of Java Interface [closed]正确使用 Java 接口 [关闭]
【发布时间】:2018-08-23 13:57:46
【问题描述】:

我最初创建了一个接口,其中包含将在两个类之间共享的所有方法,但是,我意识到我希望两个类具有相同的方法,但它们的行为会有所不同。

它们将具有相同的返回类型,但参数不同。我不知道如何实现这一点,或者即使我确实知道如何实现这一点,我也不知道这是否是处理这种情况的正确方法

基本上,我来这里是为了寻找正确的架构方法来完成我想要完成的工作,但我不知道那会是什么。我想我有 4 个问题来确定代码架构:

  1. 接口是否是正确的方法,如果是,为什么?
  2. 抽象类是正确的方法吗?如果是,为什么?
  3. 这似乎是 OOP 的一个共同主题,我的意思是拥有一个函数,你可以在给定特定类的情况下做出不同的行为。代码应该如何设计?
  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();
    }
  }
}

【问题讨论】:

  • //为什么我不能在这里@Override?​​i>因为参数的数量...
  • 你可以over-load方法setAttack()
  • 或者也许使它成为 var-args? setAttack(int.... xyz),取决于用例。
  • 如果您需要设计方面的帮助,您需要提供有关类的更多信息——它们的相关属性和方法的实现。
  • @user1803551 我已经编辑了我的代码以提供更多功能细节。你能看一下吗?

标签: java inheritance interface


【解决方案1】:

有一个概念上的误解:拥有相同方法不仅仅意味着实现具有相同名称的方法。

当您在 Java 中使用多态性和接口时,您就是在表达意图。如:实现某些接口的类必须提供相应的“功能”。换句话说:这描述了某种行为。

问题是:当接口有foo(),并且不同的类可能需要foo(X)foo(Y),那么真正的问题是:这些方法的共同点是否不仅仅是名称?!

如果是这样,一个可能的解决方案是另一个抽象层。在您的情况下,例如:

public interface AttackParameters {
...


public interface Character {  
  public void setAttack(AttackParameters parms);

或类似的东西。这个想法是再次用通用解决方案替换“特定”细节。

或者,您可以为您的setAttack() 方法使用Map&lt;String, Object&gt; 参数。换句话说:攻击参数由字符串标识。

这很好而且动态,但是它也回避了编译时安全性。稍微好一点的 map 方法不会使用字符串作为键,而是使用一些 enum 类。

【讨论】:

  • 非常感谢您提供的有用信息。如果您有时间,您是否介意为您的两个建议的替代方案提供一个小例子,如果没有,完全不用担心:)
  • 当一个接口或类是public时,每个文件只能有一个。我只是把它们放在一起作为例子。不确定我今天是否有时间添加更多示例代码。
  • 不用担心。非常感谢!非常感谢您抽出时间来回复这一事实。
  • 是的,当然,还在思考你的明智之言,哈哈。再次感谢。
【解决方案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);
}

但是,如果方法不做同样的事情,就给它们起相同的名字通常是不明智的,因为它没有明确意图。

在您的情况下,PlayerNPC 都有一个由方法填充的 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());
    }
}

在这种情况下,您有重载的方法,它们都被PlayerNPC 继承。如果每个类只能使用自己的方法,则将它们放在子类级别:

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 可用(将其放在其子类中)。

您需要问自己的是如何使用这些方法。基于此,您将知道将它们放在哪里。

【讨论】:

  • 感谢您的详细回复。为什么我的任何一个类都不允许我从接口重载方法?
  • @JonathanDeLorenzo 你的意思是覆盖?我在答案的开头解释了这一点。
  • 我明白你在说什么。方法重载实际上不需要做任何事情,但是,方法覆盖必须满足一定的标准。假设因为在方法重载时您正在创建完全不同的签名,因此您可以使用不同的参数拥有相同的名称,并且它们没有关系。
  • @JonathanDeLorenzo 查看我的编辑。
【解决方案3】:

我知道你已经接受了一个答案,但仍然想给你我的解决方案。如果你也能通过它,我将不胜感激。

在您的场景中,我们可以使用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() 和它的不同实现,并且都只使用 1 个抽象类。

【讨论】:

  • 伙计,我绝对会读这个。非常感谢您抽出宝贵时间回复。说真的,非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-11
相关资源
最近更新 更多