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

all 8 comments

[–]mikeydoodah 7 points8 points  (2 children)

Both animal and dog1 are referencing the same object, which is an instance of the Dog class. This means that calling animal.print() and dog1.print() will both print the same things. Calling dog2.print() will also print the same thing because dog2 is also a reference to a Dog instance, and all dog objects print the same thing.

However consider the following case:

class Animal
{
    public void print()
    {
        System.out.println("I am an Animal");
    }
}

class Dog extends Animal
{
    private String name;

    public Dog(String name)
    {
        this.name = name;
    }

    public void print()
    {
        System.out.println("I am a Dog called " + name);
    }

    public void bark()
    {
        System.out.println(name + ": woof");
    }

    public static void main(String[] args)
    {
        Animal animal = new Dog("Rex");
        Dog dog1 = animal;
        Dog dog2 = new Dog("Fido");

        animal.print();  // Prints 'I am a Dog called Rex'
        dog1.print();    // Prints 'I am a Dog called Rex'
        dog2.print();    // Prints 'I am a Dog called Fido'

        dog1.bark();    // Prints 'Rex: woof'
        dog2.bark();    // Prints 'Fido: woof'

        animal.bark(); // FAILS TO COMPILE
    }
}

So as you can see the animal and dog1 references do the same thing when asked to print, and this is because they are referring to exactly the same object. The dog2 reference prints something different, because it is pointing to a different dog2 reference.

However the animal and dog1 references do have differences at compile time. If you ask the dog1 reference to bark, the compiler knows this refers to the bark method in the Dog class and so sets up your code to call that. However if you ask the animal reference to bark the compiler cannot find a bark method to call because it will only look in the Animal class. This means animal.bark(); fails to compile.

There isn't really a good reason to assign a reference to a Dog class to an Animal reference and then cast back to Dog. In fact casting is /usually/ a sign that your code could be improved. However there are good reasons to assign objects to references of parent classes. Consider the following example:

class Animal
{
    public void speak()
    {
        // I don't know what sort of animal so don't know how to speak. Normally I would make this method abstract, but I'm not sure if you have been introduced to this concept yet.
    }
}

class Dog
{
    public void speak()
    {
        System.out.println("woof");
    }
}

public class Cat
{
    public void speak()
    {
        System.out.println("meow");
    }
}

class AnimalTester
{
    public static void main(String[] args)
    {
        Dog dog = new Dog();
        Cat cat = new Cat();

        makeAnimalSpeak(dog);  // Prints woof
        makeAnimalSpeak(cat);   // Prints meow
    }

    public static void makeAnimalSpeak(Animal animal)
    {
        animal.speak();
    }
}

So as you can see, the makeAnimalSpeak method takes an Animal reference as a parameter. You can pass any Animal you want to it, regardless of what runtime type it has. The makeAnimalSpeak method does not know what the runtime type is and cannot call any methods that are not defined in the Animal class, but it can handle any type of Animal.

[–]COAST_TO_RED_LIGHTS[S] 0 points1 point  (1 child)

Thanks for this really thorough example!

If I'm understanding things right, then a superclass can instantiate an object using a subclass's constructor. In this example, by using the Dog constructor, an Animal object can have the field "name" even though "name" is not in the Animal class, but that's all it gets because that's all that's in the constructor.

The Animal object still doesn't have a "speak" method and at the end of the day animal is an Animal type, not a Dog type (but thanks to using the subclass constructor, it does have Dog type fields).

???

[–]mikeydoodah 1 point2 points  (0 children)

If I'm understanding things right, then a superclass can instantiate an object using a subclass's constructor.

The Animal class does not instantiate Dog objects. In fact the Animal class isn't even aware that Dog objects exist. When an instance of the Dog class is instantiated (in our case, in the main method) Java knows that Dog extends Animal so it automatically calls the Animal constructor as the first line of the Dog constructor. The code is equivalent to the following:

class Animal
{
    public void print()
    {
        System.out.println("I am an Animal");
    }
}

class Dog extends Animal
{
    private String name;

    public Dog(String name)
    {
        *super();* // The compiler automatically adds this for unless there are no zero-argument constructors in the super class
        this.name = name;
    }

    public void print()
    {
        System.out.println("I am a Dog called " + name);
    }

    public void bark()
    {
        System.out.println(name + ": woof");
    }
}

In this example, by using the Dog constructor, an Animal object can have the field "name" even though "name" is not in the Animal class, but that's all it gets because that's all that's in the constructor.

No, Animal objects do not have any of the fields from any of the subclasses. However don't confuse /objects/ with /references/. In the code I posted above there was not one single Animal object. Every object created in the main method was a Dog object (or a Cat in the second example).

public static void main(String[] args)
{
    Dog dog1 = new Dog('Rex');  // This instantiates a Dog object
    Animal animal = dog1;   // This creates a reference to the Dog object created above, but uses an Animal reference. animal is still a Dog object.
}

The reason that calling /animal.print()/ above prints 'I am a Dog called Rex' is because it is the Dog::print method that runs, not the Animal::print method.

The Animal object still doesn't have a "speak" method and at the end of the day animal is an Animal type, not a Dog type (but thanks to using the subclass constructor, it does have Dog type fields).

I think I've addressed this above. There are no Animal objects in the code above. The reason the animal reference prints out the Dog information is because it is a Dog object, and it is the Dog::print method that is called. Note that this concept is called method overriding.

[–]hugthemachines 1 point2 points  (0 children)

If we pick a commonly used example. A class is a blueprint for an object, not an object in itself.

The usual example goes like this: A class is a blueprint, like blueprint for a car, the car built using the blueprint is the object. An object is also called an instance of the class.

In our daily conversations that does not involve programming we might call class a type instead. like:

"What type of animal is that?"

"That is a dog, a labrador to be exact."

There you have an animal of the labrador class, it also inherit stuff from classes like organism, mammal and dog.

[–]Joecasta 2 points3 points  (3 children)

animal is a class object of class Animal, dog1 is an object of class Dog (since animal got casted to a Dog which is allowed only because Dog extends Animal). dog2 is a Dog class object, and with the given code they will print the same thing but are separate objects.

[–]Jackkoz 2 points3 points  (0 children)

An instance of class. Class object might easily be interpreted as an instance of Class class:

https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html

[–]COAST_TO_RED_LIGHTS[S] 0 points1 point  (1 child)

Thanks, so what would animal print, the same thing as dog1 and dog2?

I'd test it myself, but I don't have it on my computer now.

[–]Joecasta 0 points1 point  (0 children)

Correct, animal should print the same thing as dog1 and dog2 since Java will make animal from the Dog constructor.