【问题标题】:Is it possible to extend instance of Class是否可以扩展类的实例
【发布时间】:2017-03-06 12:07:17
【问题描述】:

例如,我有动物类和扩展动物的类猫(鸟、狗、鱼...)。现在我想声明类 Pet,它也是 Animal,我希望它是从任何现有的 Animal 构建的,例如猫。

所以我需要这样的构造函数:

class Pet extends Animal{
    private String nickname;
    private Animal kind;

    Pet(Animal a, String nickname){
        <...>;
        this.nickname = nickname;
    }
}

编辑: 我想要这样的东西:

Cat cat = new Cat();
Animal pet = new Pet(cat, "Foo");
if (pet instanceof Pet){
    if (pet.kind instanceof Cat){
         Pet.sayMeow();
    }
}

这是否意味着我只需要 <...> 中的Animal()(受保护的)构造函数?

【问题讨论】:

  • 问问自己:Pet 应该如何处理传递的Animal?您目前对此无能为力,这就是为什么这个问题不清楚(不清楚您通过 Animal 的原因是什么)。
  • @Tom 所说的:有关您要实现的目标的更多信息会很好。目前,这听起来像是您可以使用 interface 的东西。
  • 如果您说您想获取Animal 的现有实例并将该实例 转换为Pet 追溯,您不能这样做。您可以创建一个从 Animal 实例初始化的新 Pet,但它将是独立的。实例的类在创建时被锁定。 (我们可以对那个实例有不同的[接口]视图,一些代码可能只知道它是Animal,而其他代码可能知道它是Cat,但是instance有一个特定的类集当它被创建时。)
  • 闻起来有点像decorator。从代码中,我猜您只是想将“昵称”属性添加到任何Animal。对吗?
  • 我刚刚在您的编辑中看到的内容:您为什么要在 Animal 类中拥有一个 Animal 成员?更一般地说,kind 到底应该是/做什么?你看,你是在说“我想要这样的东西”,但我们很难说为什么你想要那个。

标签: java oop


【解决方案1】:

即使在您编辑之后,我也不清楚您的意图。似乎您只是想找到一种方法,让您在运行时说:“这只猫以前是一只流浪猫,但现在它成了某人的宠物!”。

如果正确,您可以选择一个非常简单直接的解决方案 - 将此功能添加到Animal 类:

public class Animal {

  private boolean isPet = false;
  private String nickname = "";

  public Animal() {
    /*...*/
  }

  public makeStray() {
    isPet = false;
    nickname = "";
  }

  public makePet(String nickname) {
    isPet = true;
    this.nickname = nickname;
  }

  public boolean isPet() {
    return isPet;
  }

  public void makeNoise() {
    /* Override in child classes */
  }

}

根据您的示例,您可以这样做:

Animal cat = new Cat();
cat.makePet("Foo");
if (cat.isPet()) { // Apparently, only pet cats ever meow.
     cat.makeNoise(); // Cats will meow, dogs will bark, ...
}

但是请注意,这种编码方式可能会迅速膨胀一个类。这真的取决于你打算用它做什么,除了这个。我会说这是 quick 'n' dirty 解决方案。

如需更复杂的解决方案,请查看其他答案。

EDIT1:正如 Fildor 正确指出的那样,拥有 sayMeow() 方法并不是一个好主意。最好在Animal 中有一个makeNoise() 方法并在子类中覆盖它以获得不同种类动物的特定行为。现在,如果您不想实际创建Animal 类的实例,您还可以创建abstract 类以及makeNoise() 方法。这将确保每个子类都必须实现makeNoise() 方法。或者,如果方法没有被覆盖,你可能对静音动物的默认行为没问题。

EDIT2This answer 到相关问题可能会更清楚地了解您的情况。它是关于 C# 的,但原则转化为 Java。

【讨论】:

  • 我重新编辑了我的编辑,现在它正确显示了我想要的内容
  • 并非如此。您向我们展示了一些您想要工作的代码,但没有展示其背后的intent。如果我没看错的话,其意图是:“我希望能够判断某种动物是否是宠物,并据此采取行动”。上面显示的代码可以做到这一点。
【解决方案2】:

您所描述的完全可能,通常称为“装饰器模式”。

仅链接的答案不好,以后有时间我会详细说明。

同时,维基百科有更多信息:Decorator Pattern


1 Cat cat = new Cat();
2 Animal pet = new Pet(cat, "Foo");
3 if (pet instanceof Pet){
4    if (pet.kind instanceof Cat){
5         Pet.sayMeow();
6    }
7 }

这有个缺点,你需要使用instanceOf。通常,你会让你的Animal 类有一个方法——我们称之为makeNoise。大概是抽象的。您的 Animal-Implementations (Cat, Dog...) 然后将覆盖该方法以产生它们各自的噪音 ("Bark","Miow" ...)。

在 sn-p 中,似乎只有宠物可以发出声音……这使得它有点复杂,因为有多种方法可以做到这一点。

您可以让装饰器保存声音并覆盖 makeNoise 以发出声音。像这样:

Cat catInstance = new Cat();
catInstance.makeNoise(); // Default impl: NOP => "" - no sound.
Animal pet = new Pet( catInstance, "Mieow" );
pet.makeNoise(); // => "Mieow"

这一切的重点是:你要避免使用instanceof。你不在乎动物是猫狗、宠物猫还是宠物狗。如果被告知,他们会发出正确的声音。因此,您可以收集“Animal”并告诉他们所有人“makeNoise”,每个人都会保持沉默、吠叫或喵喵叫,而无需您关心它们是否是宠物或Animal 的哪个特定孩子。

编辑:再次阅读我的答案,它更像是一种策略(策略模式)而不是装饰器。 Policy 改变了某事的完成方式,而 Decorator 将添加该功能。

所以要成为一个真正的装饰者,这意味着makeNoise 将在Pet 的接口中。这意味着您不能在 Animals 上调用该方法。

这就是为什么我将我的建议从“装饰者”改为“Strategy”模式。

上面的例子仍然成立。您可能会有一个“默认”策略,并通过使用类似装饰器的实现方法注入“宠物”策略。

当然,所有这些也可以通过不同的方式完成,以更严格地实现该模式。

最后,if( x instanceof X) ... 也总是叮叮当当“访客”-Bell。

【讨论】:

  • makeNoise() 的好消息——之前完全忽略了这一点。现在相应地编辑了我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-21
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 2012-03-12
相关资源
最近更新 更多