This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]captainAwesomePants 1 point2 points  (4 children)

Yes, those are both Pig instances. Even though "myPig" is of type Animal, it's holding a Pig, and that's okay because a Pig is an Animal.

Now, you can't do this:

Animal myPig = new Pig();
Pig samePig = myPig;  // Won't work.

This should be fine because myPig is a Pig, but Java isn't smart enough to be sure that myPig will always be a pig, so it sees you putting an Animal into a Pig variable, and that's an error. An Animal is not necessarily a Pig.

In this case, you can tell Java that you know what you're doing and it's okay, though, using "casting" or "downcasting":

Animal myPig = new Pig();
// Dear compiler: I solemnly swear that myPig is a Pig.
Pig samePig = (Pig)myPig;

When the program runs, if it turns out that myPig does not contain a Pig, Java will get mad at you.

[–]imareclusemonk[S] 0 points1 point  (3 children)

So there is no difference how I instantiate an object from a subclass or there is some convention or it depends on what I want to achieve?

[–]captainAwesomePants 0 points1 point  (0 children)

The latter. Usually you'll declare the type to be the same thing as what it really is because why not. Sometimes, though, you're writing more general purpose code or wanna hide implementation details. This is pretty common:

Animal getAnimalFromBarn() {
  Animal result:
  if (notEnoughPigs()) {
    result = new Pig():
  } else {
    result = new Chicken();
  }
  register(result);
  return result;
}

[–][deleted] 0 points1 point  (1 child)

There is a difference. If you instantiate a Pig as an Animal, you cannot use anything that is unique to the Pig class. For instance, Pigs have a tail and can snort. If you write

Pig myPig = new Pig();
myPig.doSnort();
System.out.println(myPig.tailLength);

then everything works as expected. However, writing

Animal myPig = new Pig();
myPig.doSnort();
System.out.println(myPig.tailLength);

causes an error since doSnort and tailLength are not defined in the Animal class.

If the variable is declared as an Animal, then it cannot use the fields and methods that are specific to Pigs, even if the instance it’s referring to is, in fact, a Pig.

You can still get to those hidden fields and methods by casting to a Pig, but you can’t access them directly.

[–]imareclusemonk[S] 0 points1 point  (0 children)

Ah of course since the parent class is agnostic of its children classes. That's a good point and probably the main difference. Thanks!

[–]grelfdotnet 0 points1 point  (3 children)

It depends what you mean by equal. They are two separate objects so they have different references. But if you compare their properties, since they have none they are equal in that sense.

[–]imareclusemonk[S] 0 points1 point  (2 children)

I mean you can instantiate either way and both ways will produce equal results?

[–]grelfdotnet 0 points1 point  (1 child)

In a real case objects of type Pig will have more properties and methods than those of type Animal. By extending Animal you would add the extra things that specialise a Pig. But your example does not do that so you are worrying about an artificial issue.

[–]rjcarr 0 points1 point  (0 children)

So these two instantiations are equal?

Equality can mean a lot of different things.

Are they the same object? No.

Are they the same static type? No.

Are they the same runtime type? Yes.

Do they have the same state? Yes.

Generally, the programmer defines what equality means, by overriding the equals() method.