简体   繁体   中英

Understanding inheritance and abstract classes in Java

Alright so I've looked for documentation by googleing, however I haven't found any that really describes what I'm looking to answer, so here I am asking you guys.

So I get inheritance, and how it works. What I'm having a problem with is sometimes I see a object originally defined as one type, and set to a different type, and I don't understand exactly what's happening. Here's an example:

Say I have a class animal, and classes cat and dog which extend animal. Cat, animal and dog all have a method speak() which for cat prints "meow" and for dog prints "woof" and for animal "can't speak".

Alright so finally here's my question. What exactly happens if a make a cat (c) and then run Animal a = c;? What happens if I run a.speak();? Which speak method is called? What exactly has happened when I change types like that? Will I ever have any real reason to use this?

As far as abstract methods go, my question is what exactly is the point of having them? In the examples I've seen they've been put in super classes and the classes under them define the exact behavior. By putting an abstract method in a super class is one requiring all the classes under it to implement it?

Thanks for all your help!

What exactly happens if a make a cat (c) and then run Animal a = c;? What happens if I run a.speak();? Which speak method is called? What exactly has happened when I change types like that? Will I ever have any real reason to use this?

Always the method of the real class, for example in this case, the speak() method of the cat.

As far as abstract methods go, my question is what exactly is the point of having them?

They make sure that, for example, every animal has a method walk() that you can call on every animal. It's a warranty that says "every Animal object has this method, you don't have to care about it".

In the examples I've seen they've been put in super classes and the classes under them define the exact behavior. By putting an abstract method in a super class is one requiring all the classes under it to implement it?

To implement it or to be abstract, too, yes.

Cat c = new Cat(); Animal a = c; a.speak() will print meow.

please check java polymorphism out.

about the abstract class:

When an abstract class is subclassed, the subclass usually provides implementations for all of the abstract methods in its parent class. However, if it does not, the subclass must also be declared abstract.

The JLS, section 5.2 explains why Cat is assignable to Animal . (Note that Animal is not implicitly assignable to Cat because Cat is a "more specific" kind; Cat is a subtype of Animal and Animal is the supertype of Cat )

Assignment of a value of compile-time reference type S (source) to a variable of compile-time reference type T (target) is checked as follows:

  • If S is a class type:
    • If T is a class type, then S must either be the same class as T, or S must be a subclass of T, or a compile-time error occurs.
    • If T is an interface type, then S must implement interface T, or a compile-time error occurs.
    • If T is an array type, then a compile-time error occurs.
  • If S is an interface type:
    • If T is a class type, then T must be Object , or a compile-time error occurs.
    • If T is an interface type, then T must be either the same interface as S or a superinterface of S, or a compile-time error occurs.
    • If T is an array type, then a compile-time error occurs.
  • If S is an array type SC[], that is, an array of components of type SC:

[omitted for brevity]

Say I have a class animal, and classes cat and dog which extend animal. Cat, animal and dog all have a method speak() which for cat prints "meow" and for dog prints "woof" and for animal "can't speak".

Alright so finally here's my question. What exactly happens if a make a cat ( c ) and then run Animal a = c; ? What happens if I run a.speak(); ? Which speak() method is called? What exactly has happened when I change types like that? Will I ever have any real reason to use this?

Objects in Java know exactly what type they were created as; it's effectively a hidden field (which you can retrieve with the Object.getClass() method). Moreover, all non-static method resolution starts with the method definitions of the most specific class and proceeds towards the most generic class ( Object ); since there is only ever single inheritance of implementation in Java, this is a simple search. Cat knows it's a subtype of Animal , which is a subtype of Object , and c knows that it's a Cat independent of what type the variable is.

When you do the assignment, the compiler checks if the known type of the value being assigned is the type being assigned to or one of its subtypes . If it is, the assignment works. If it isn't, you'll need an explicit cast (which puts in a proper type-check at runtime; casts can't break Java's type-system, they can just make it ugly). It doesn't change the fact that method lookup is still done dynamically and the object still knows what type it is really; all the program is doing is neglecting some information. If you know C++, then think of Java as having only virtual methods (and static methods) and it's very simple to handle the lookups into the vtables because there's no problem with inheritance diamonds or other evil cases.

When working with implementations of an interface, it's almost the same except that a more complex lookup is done (ie, first the index in the vtable is looked up before proceeding as before). However, to have an object that implements the interface means that there must be some class that fully implements the interface, by which point it's all relatively simple again. And remember, all the really complicated stuff is done at compilation time; at run time, things are relatively straight-forward in all cases.

So, will you make use of any of this? Well, you should (and you'll actually find it very hard to avoid in real code). It's good style to think in terms of having a contract defined by an interface or superclass, where the subtypes obey the contract without the caller having to know about the details. Java libraries use this very heavily, particularly since the visibility of the type details of the contract and its fulfillment can be different. All the client code knows is that the object obeys the contract, that it is of the given type.

Depending on what your background is, C++ or Java things can get very confusing.

In C++ there is the concept of virtual functions which are looked up at run time to decide which actual class that function belongs to. There are also non-virtual functions which will be called based on the variable type.

In Java though all methods are essentially virtual, which means that Java methods are always looked up at run-time, a process called Runtime Polymorphism

A benefit of this is something like this

class Animal{
    public void type(){
        System.out.println("animal");

    }
}

class Dog extends Animal{
    public void type(){
        System.out.println("dog");
    }
}

class Cat extends Animal{
    public void type(){
        System.out.println("cat");
    }
}

public class Driver{
    public static void main(String[] args){
        Animal[] animals = new Animal[3];
        animals[0] = new Animal();
        animals[1] = new Dog();
        animals[2] = new Cat();

        for(Animal animal: animals){
            animal.type();
        }
    }
}

This will output

animal
dog
cat

To my understanding, you use interfaces to decouple your code. You want to program against interfaces, instead of implementations: What does it mean to "program to an interface"? . You would use an abstract class to implement the functionlity which is trivial for all implementing classes, so you don't need to write it in all implementing classes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM