1

For example i have class Animal, and class Cat (Bird, Dog, Fish ...) which extends Animal. Now I want to declare class Pet, which is also Animal, and I want it to be built from any existing Animal, e.g. Cat.

So I need constructor like this:

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

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

EDIT: I want something like this:

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

Is that means that I just need Animal()(which is protected) constructor in <...> ?

Noqrax
  • 1,049
  • 2
  • 8
  • 15
  • 3
    Ask yourself: what is `Pet` supposed to do with the passed `Animal`? You currently do nothing with that, that's why this question is unclear (unclear what your reason for passing `Animal` is). – Tom Mar 06 '17 at 12:09
  • What @Tom said: some more info on what you're trying to achieve would be nice. Currently, this sounds like something you could use an `interface` for. – domsson Mar 06 '17 at 12:11
  • 2
    If you're saying you want to take an existing instance of `Animal` and convert **that instance** into a `Pet` retroactively, you can't do that. You can create a new `Pet` that's initialized from an `Animal` instance, but it will be separate. The class of an instance is locked in when it is created. (We can have different views of [interfaces to] that instance, some code might only know it's an `Animal` and other code may know it's a `Cat`, but the *instance* has a specific class set when it's created.) – T.J. Crowder Mar 06 '17 at 12:15
  • Smells like kind of a [decorator](https://en.wikipedia.org/wiki/Decorator_pattern). From the code, I guess you simply want to add the "Nickname" poperty to any `Animal`. Is that correct? – Fildor Mar 06 '17 at 12:17
  • Something I just saw now in your edit: why would you want to have a `Animal` member inside of the `Animal` class? And more generally, what exactly is `kind` supposed to be/do? You see, you're saying *"I want something like this"* but it is hard for us to tell *why* you want that. – domsson Mar 06 '17 at 13:47
  • A pet isn't necessarily an animal, and doesn't really fit into an animal taxonomy. It's really more a role an animal (or a rock) can fill. It has attributes that are not inherent to animalness, like an owner and a vaccination record. Better to make it its own type, with an `Animal` instance member. – Lew Bloch Mar 06 '17 at 15:18

2 Answers2

1

What you describe is perfectly possible and commonly known as "Decorator Pattern".

Link-Only answers are bad, I will elaborate later when I have more time.

Meanwhile, Wikipedia has more Info: 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 }

This has the disadvantage that you need to use instanceOf. Usually, you would have your Animal class have a method - let's call it makeNoise. Probably abstract. You Animal-Implementations ( Cat, Dog... ) then would override that method to make their respective noise ("Bark","Miow" ...).

In the snippet, it seems only Pets can make noises ... that makes it a little bit more complicated because there would be various ways to do that.

You could have the decorator save a sound and override the makeNoise to make that sound. Like so:

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

The point of all this is: You want to avoid using instanceof. You don't care if the Animal is a Cat a Dog, a Pet-Cat or a Pet-Dog. They shall make their correct sounds if and when told to. So you could have a Collection of "Animal"s and tell them all to "makeNoise" and each will remain silent, bark or mieow without you having to care for if they are pets or which specific child of Animal.

EDIT: Reading my answer again, it is more like a Policy (Strategy pattern) than a Decorator. The Policy changes how something is done, while the Decorator would add the feature.

So to be a true Decorator, it would mean that makeNoise would be in the interface of Pet. Which means you couldn't call that method on Animals.

That is why I change my suggestion from "Decorator" to "Strategy" pattern.

The example above still holds. You would kind of have a "default" Strategy and inject the "Pet"-Strategy by using a Decorator-Like implementation approach.

Of course all of this could also be done differently to implement the pattern more strictly.

At last, if( x instanceof X) ... always jingles the "Visitor"-Bell, too.

Fildor
  • 14,510
  • 4
  • 35
  • 67
  • Good catch on the `makeNoise()` - completely ignored that before. Now edited my answer accordingly. – domsson Mar 06 '17 at 12:52
1

Even after your edit, your intentions aren't really clear to me. It seems as if you're simply looking to find a way that allows you, at runtime, to say: "This cat was a stray before but it just became someone's pet now!".

If that's right, you could go for a very simple and straight forward solution - add this feature right to the Animal class:

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 */
  }

}

According to your example, you could then simply do:

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

Note, however, that this way of coding can quickly bloat up a class. It really depends on what you're planning to do with it other than this. I'd say this is the quick 'n' dirty solution.

For more sophisticated solutions, check the other answer(s).

EDIT1: As Fildor correctly pointed out, having a method sayMeow() isn't such a good idea. Better have a makeNoise() method in Animal and override it in the child classes to get the specific behavior for the different kind of Animals. Now, if you never want to actually create an instance of the Animal class, you could also make the class abstract, as well as the makeNoise() method. This would ensure that every child class has to implement the makeNoise() method. Or maybe you are fine with the default behavior of mute animals if the method isn't overridden.

EDIT2: This answer to a related questions might shed more light on your situation. It is about C#, but the principles translate to Java.

Community
  • 1
  • 1
domsson
  • 4,553
  • 2
  • 22
  • 40
  • I reedited my edit so, now it shows properly what I want – Noqrax Mar 06 '17 at 12:40
  • Not really. You showed us some code you would want to work but not the *intent* behind it. If I read it correctly, the intent is: "I want to be able to tell if a certain animal is a pet or not and take actions based on it". That would be possible with the code shown above. – domsson Mar 06 '17 at 12:44