Similarly in object-oriented programming, you may want to model abstract concepts but you don't want to be able to create an instance of it. For example, the Number class in the java.lang package represents the abstract concept of numbers. It makes sense to model numbers in a program, but it doesn't make sense to create a generic number object. Instead, the Number class makes sense only as a superclass to classes like Integer and Float which implement specific kinds of numbers. Classes such as Number, which implement abstract concepts and should not be instantiated, are called abstract classes. An abstract class is a class that can only be subclassed--it cannot be instantiated.
To declare that your class is an abstract class, use the keyword abstract
before the class
keyword in your class declaration:
If you attempt to instantiate an abstract class, the compiler will display an error similar to the following and refuse to compile your program:abstract class Number { . . . }
AbstractTest.java:6: class AbstractTest is an abstract class. It can't be instantiated. new AbstractTest[]; ^ 1 error
An abstract class may contain abstract methods, that is, methods with no implementation. In this way, an abstract class can define a complete programming interface thereby providing its subclasses with the method declarations for all of the methods necessary to implement that programming interface. However, the abstract class can leave some or all of the implementation details of those methods up to its subclasses.Let's look at an example of when you might want to create an abstract class with an abstract method in it. In an object-oriented drawing application, you can draw circles, rectangles, lines, Beziers, and so on. Each of these graphic objects share certain states [position, bounding box] and behavior [move, resize, draw]. You can take advantage of these similarities and declare them all to inherit from the same parent object--GraphicObject.
However, the graphic objects are also substantially different in many ways: drawing a circle is quite different from drawing a rectangle. The graphics objects cannot share these types of states or behavior. On the other hand, all GraphicObject's must know how to draw themselves; they just differ in how they are drawn. This is a perfect situation for an abstract superclass.
First you would declare an abstract class, GraphicObject, to provide member variables and methods that were wholly shared by all subclasses such as the current position and the
moveTo[]
method. GraphicObject would also declare abstract methods for methods, such asdraw[]
, that needed to be implemented by all subclasses, but implemented in entirely different ways [no suitable default implementation in the superclass makes sense]. The GraphicObject class would look something like this:Classes in Java exist in a hierarchy. A class in Java can be declared as a subclass of another class using the
0 keyword. A subclass inherits variables and methods from its superclass and can use them as if they were declared within the subclass itself:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
class
Animal
{
float
weight
;
...
void
eat
[]
{
...
}
...
}
class
Mammal
extends
Animal
{
// inherits weight
int
heartRate
;
...
// inherits eat[]
void
breathe
[]
{
...
}
}
In this example, an object of type
1 has both the instance variableCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
2 and the methodCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
3. They are inherited fromCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
A class can extend only one other class. To use the proper terminology, Java allows single inheritance of class implementation. Later in this chapter, we’ll talk about interfaces, which take the place of multiple inheritance as it’s primarily used in other languages.
A subclass can be further subclassed. Normally, subclassing specializes or refines a class by adding variables and methods [you cannot remove or hide variables or methods by subclassing]. For example:
class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
The
5 class is a type ofCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
1 that is ultimately a type ofCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 objects inherit all the characteristics ofCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
1 objects and, in turn,Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 objects.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 also provides additional behavior in the form of theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
2 method and theclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
3 variable. We can denote the class relationship in a diagram, as shown in .class
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
A subclass inherits all members of its superclass not designated as
4. As we’ll discuss shortly, other levels of visibility affect which inherited members of the class can be seen from outside of the class and its subclasses, but at a minimum, a subclass always has the same set of visible members as its parent. For this reason, the type of a subclass can be considered a subtype of its parent, and instances of the subtype can be used anywhere instances of the supertype are allowed. Consider the following example:class
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
Figure 6-1. A class hierarchy
The
5 instanceCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
6 in this example can be assigned to theclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
4 type variableCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
8 becauseclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
5 is a subtype ofCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4. Similarly, any method accepting anCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 object would accept an instance of aCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 or anyCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
1 type as well. This is an important aspect of polymorphism in an object-oriented language such as Java. We’ll see how it can be used to refine a class’s behavior, as well as add new capabilities to it.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
In Chapter 5, we saw that a local variable of the same name as an instance variable shadows [hides] the instance variable. Similarly, an instance variable in a subclass can shadow an instance variable of the same name in its parent class, as shown in . We’re going to cover the details of this variable hiding now for completeness and in preparation for more advanced topics, but in practice you should almost never do this. It is much better in practice to structure your code to clearly differentiate variables using different names or naming conventions.
In , the variable
2 is declared in three places: as a local variable in the methodCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 of the classint
s
=
super
.
sum
;
1, as an instance variable of the classCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
1, and as an instance variable of the classCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4. The actual variable selected when you reference it in the code would depend on the scope in which we are working and how you qualify the reference to it.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
Figure 6-2. The scope of shadowed variables
In the previous example, all variables were of the same type. A slightly more plausible use of shadowed variables would involve changing their types. We could, for example, shadow an
9 variable with aint
s
=
super
.
sum
;
0 variable in a subclass that needs decimal values instead of integer values. We can do this without changing the existing code because, as its name suggests, when we shadow variables, we don’t replace them but instead mask them. Both variables still exist; methods of the superclass see the original variable, and methods of the subclass see the new version. The determination of what variables the various methods see occurs at compile time.DecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
Here’s a simple example:
class
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
In this example, we shadow the instance variable
1 to change its type fromDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
9 toint
s
=
super
.
sum
;
0.[] Methods defined in the classDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4 see the integer variableDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
1, while methods defined inDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
6 see the floating-point variableDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
1. However, both variables actually exist for a given instance ofDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
6, and they can have independent values. In fact, any methods thatDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
6 inherits fromDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4 actually see the integer variableDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
1.DecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
Because both variables exist in
6, we need a way to reference the variable inherited fromDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4. We do that using theDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4 keyword as a qualifier on the reference:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
int
s
=
super
.
sum
;
Inside of
6, theDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4 keyword used in this manner selects theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
1 variable defined in the superclass. We’ll explain the use ofDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4 more fully in a bit.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
Another important point about shadowed variables has to do with how they work when we refer to an object by way of a less derived type [a parent type]. For example, we can refer to a
6 object as anDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4 by using it via a variable of typeDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
4. If we do so and then access the variableDecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
1, we get the integer variable, not the decimal one:DecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
DecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
The same would be true if we accessed the object using an explicit cast to the
4 type or when passing an instance into a method that accepts that parent type.DecimalCalculator
dc
=
new
DecimalCalculator
[];
IntegerCalculator
ic
=
dc
;
int
s
=
ic
.
sum
;
// accesses IntegerCalculator sum
To reiterate, the usefulness of shadowed variables is limited. It’s much better to abstract the use of variables like this in other ways than to use tricky scoping rules. However, it’s important to understand the concepts here before we talk about doing the same thing with methods. We’ll see a different and more dynamic type of behavior when methods shadow other methods, or to use the correct terminology, override other methods.
In Chapter 5, we saw that we could declare overloaded methods [i.e., methods with the same name but a different number or type of arguments] within a class. Overloaded method selection works in the way we described on all methods available to a class, including inherited ones. This means that a subclass can define additional overloaded methods that add to the overloaded methods provided by a superclass.
A subclass can do more than that; it can define a method that has exactly the same method signature [name and argument types] as a method in its superclass. In that case, the method in the subclass overrides the method in the superclass and effectively replaces its implementation, as shown in . Overriding methods to change the behavior of objects is called subtype polymorphism. It’s the usage most people think of when they talk about the power of object-oriented languages.
Figure 6-3. Method overriding
In ,
1 overrides theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 method ofclass
Cat
extends
Mammal
{
...
@Override
void
sleep
[]
{
...
}
}
4, perhaps to specialize the method for the behavior of mammals giving birth to live young.[] TheCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 object’s sleeping behavior is also overridden to be different from that of a generalCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4, perhaps to accommodate cat naps. TheCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 class also adds the more unique behaviors of purring and hunting mice.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
From what you’ve seen so far, overridden methods probably look like they shadow methods in superclasses, just as variables do. But overridden methods are actually more powerful than that. When there are multiple implementations of a method in the inheritance hierarchy of an object, the one in the “most derived” class [the furthest down the hierarchy] always overrides the others, even if we refer to the object through a reference of one of the superclass types.[]
For example, if we have a
5 instance assigned to a variable of the more general typeCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4, and we call itsCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
2 method, we still get thestatic
final
boolean
DEBUG
=
false
;
...
final
void
debug
[
String
message
]
{
if
[
DEBUG
]
{
System
.
err
.
println
[
message
];
// do other stuff
...
}
}
2 method implemented in thestatic
final
boolean
DEBUG
=
false
;
...
final
void
debug
[
String
message
]
{
if
[
DEBUG
]
{
System
.
err
.
println
[
message
];
// do other stuff
...
}
}
5 class, not the one inCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
In other words, for purposes of behavior [invoking methods], a
5 acts like aCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5, regardless of whether you refer to it as such. In other respects, the variableCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
8 here may behave like anclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
4 reference. As we explained earlier, access to a shadowed variable through anCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 reference would find an implementation in theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 class, not theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 class. However, because methods are located dynamically, searching subclasses first, the appropriate method in theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5 class is invoked, even though we are treating it more generally as anCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 object. This means that the behavior of objects is dynamic. We can deal with specialized objects as if they were more general types and still take advantage of their specialized implementations of behavior.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
A common programming error in Java is to accidentally overload a method when trying to override it. Any difference in the number or type of arguments [the method signature] produces two overloaded methods instead of a single, overridden method. The new annotations syntax in Java 5.0 provides a way to get the compiler to help with this problem. An annotation, as we’ll describe in Chapter 7, allows us to add special markers or metadata to source code that can be read by the compiler or runtime tools. One of the standard annotations that Java defines is called
5 and it tells the compiler that the method it marks is intended to override a method in the superclass. The compiler then warns if the method doesn’t match. For example, we could specify that thepublic
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
2 method of ourstatic
final
boolean
DEBUG
=
false
;
...
final
void
debug
[
String
message
]
{
if
[
DEBUG
]
{
System
.
err
.
println
[
message
];
// do other stuff
...
}
}
5 class overrides one in a superclass like so:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
class
Cat
extends
Mammal
{
...
@Override
void
sleep
[]
{
...
}
}
Overridden methods and dynamic binding
In a previous section, we mentioned that overloaded methods are selected by the compiler at compile time. Overridden methods, on the other hand, are selected dynamically at runtime. Even if we create an instance of a subclass our code has never seen before [perhaps a new class loaded over the network], any overriding methods that it contains are located and used at runtime, replacing those that existed when we last compiled our code.
In contrast, if we created a new class that implements an additional, more specific, overloaded method, and replace the compiled class in our classpath with it, our code would continue to use the implementation it discovered originally. This situation would persist until we recompiled our code along with the new class. Another effect of this is that casting [i.e., explicitly telling the compiler to treat an object as one of its assignable types] affects the selection of overloaded methods at compile time but not overridden methods.
In practice what we’ve just described is not something you need to worry about often, but it’s important in understanding what the virtual machine does and does not do at runtime.
Static methods don’t belong to any object instance; they are accessed directly through a class name, so they are not dynamically selected at runtime like instance methods. That is why static methods are called “static”; they are always bound at compile time.
A static method in a superclass can be shadowed by another static method in a subclass, as long as the original method was not declared final. However, both methods are always accessible directly via their respective class names. You can’t “override” a static method with an instance method. In other words, you can’t have a static method and instance method with the same signature in the same class hierarchy.
final methods and performance
In languages like C++, the default is for methods to act like shadowed variables, so you have to declare explicitly the methods you want to be dynamic [or, as C++ terms them, virtual]. In Java, instance methods are, by default, dynamic. But you can use the
8 modifier to declare that an instance method can’t be overridden in a subclass, and it won’t be subject to dynamic binding.public
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
We have seen
8 used with variables to effectively make them constants. When applied to a method,public
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
8 means that its implementation is constant—no overriding allowed.public
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
8 can also be applied to an entire class, which means the class can’t be subclassed.public
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
In the old days, dynamic method binding came with a significant performance penalty, and some people are still inclined to use the
8 modifier to guard against this. Modern Java runtime systems eliminate the need for this kind of tweaking. A profiling runtime can determine which methods are not being overridden and “optimistically” inline them, treating them as if they were final until it becomes necessary to do otherwise. As a rule, you should use thepublic
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
8 keyword when it is correct for your program’s structure, not for performance considerations.public
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
In some older versions of Java, the javac compiler can be run with a -O switch, which tells it to perform certain optimizations, like inlining, statically. Most of these optimizations are now done at runtime by smarter VMs, so switches like this are generally not necessary.
Another kind of optimization allows you to include debugging code in your Java source without incurring a size or performance penalty. Although Java doesn’t have a preprocessor to explicitly control what source is included, you can get some of the same effects by making a block of code conditional on a constant [i.e.,
04 andclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
8] variable. The Java compiler is smart enough to remove this code when it determines that it won’t be called. For example:public
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
static
final
boolean
DEBUG
=
false
;
...
final
void
debug
[
String
message
]
{
if
[
DEBUG
]
{
System
.
err
.
println
[
message
];
// do other stuff
...
}
}
In this case, the compiler can recognize that the condition on the
06 variable is alwaysclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
07, and the body of theclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
08 method will be optimized away. With a modern compiler, the method call might even be optimized away entirely.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
Note that this kind of debugging code is useful for purposes such as logging. In contrast to assertions, which we covered in Chapter 4, which are supposed to be yes/no tests that guarantee the correctness of your program logic, these conditional blocks of code might do expensive formatting or other output processing that is useful during develoment but you don’t wish to have around in the final product.
Method selection revisited
By now you should have a good, intuitive feel for how methods are selected from the pool of potentially overloaded and overridden method names of a class. If, however, you are dying for more detail, we’ll provide it now.
In a previous section, we offered an inductive rule for overloaded method resolution. It said that a method is considered more specific than another if its arguments are assignable to the arguments of the second method. We can now expand this rule to include the resolution of overridden methods by adding the following condition: to be more specific than another method, the type of the class containing the method must also be assignable to the type of the class holding the second method.
What does that mean? Well, the only classes whose types are assignable are classes in the same inheritance hierarchy, meaning that we’re talking about the set of all methods of the same name in a class or any of its parent or child classes. Because subclass types are assignable to superclass types, but not vice versa, the resolution is pushed in the way that we expect down the chain toward the subclasses. This effectively adds a second dimension to the search, in which resolution is pushed down the inheritance tree toward more refined classes and, simultaneously, toward the most specific overloaded method within a given class.
Exceptions and overridden methods
An overriding method may change the behavior of an object, but in some ways, it must still fulfill the contract of the original method with the user. Specifically, an overriding method must adhere to the
09 clause of the original method. The new method cannot throw new types of checked exceptions. It can only declare that it throws exception types assignable to those thrown by the method in the parent class; that is, it may declare that it throws the same types of exceptions or subtypes of those declared by the original method. If the new method does not throw any of the checked exceptions of the original, it does not have to declare them and callers of the method via the subclass do not have to guard against them. [In this way, you can override a method to “handle” exceptions for the user.]class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
So the new method may declare exactly the same checked exceptions as the original, or it has the option to refine those types by declaring that it throws more specific subtypes than the overridden method. This is not the same as just saying that the method can simply throw subtypes of its declared exceptions; any method can do that. The new method can actually redefine the
09 clause of the method to be more specific. This technique is called covariant typing of theclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
09 clause, which means that the exception types against which the user must guard change to become more refined with the subtype.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
Let’s quickly review what the
09 clause really means. If a method declares that it can throw anclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
13, it is really saying that it can throw exceptions of typeclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
13 or its subtypes. For example,class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
15 is a type ofclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
13. A method declaring that it can throwclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
13 could actually throwclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
15 or any other subtype ofclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
13 at runtime:class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
public
void
readFile
[]
throws
IOException
{
...
if
[
error
]
throw
new
FileNotFoundException
[
filename
];
}
When we call this method, the compiler will ensure that we allow for the possibility of any kind of
13, using either aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
21 block or by throwing the exception from our own method.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
When we override a method in a subclass, we get an opportunity to rewrite the
09 clause of the method a bit. The new method must still be backward-compatible with the original, so any checked exceptions it throws must be assignable to those thrown by the overridden method. But we can be more specific if we want, refining the type of exception to go along with the new method’s behavior. For example:class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
0class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
In this code,
4 specifies that it can throw anCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
24 from itsclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
3 method.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
26 is a subclass ofclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
4, so itsCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
3 method must also be able to throw anCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
24. However,class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
26’sclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
3 method actually declares that it throws a more specific exception:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
32. It can do this becauseclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
32 is a subtype ofclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
24. If we are working with anclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
26 type directly, the compiler will allow us to catch just theclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
32 and not require us to guard against the more generalclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
24:class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
1class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
On the other hand, if we don’t care why the food is inedible, we’re free to guard for the more general
24 alone and treat it as any otherclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
4.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
To sum up, an overriding method can refine not only the behavior of the parent method, but also the type of checked exceptions it throws. Next, we’ll talk about overridden methods that change their return type in exactly the same way.
Return types and overridden methods
For a method to qualify as an overridden method in a subclass, it must have exactly the same number and types of arguments. It must have the same “inputs,” as it were. As we saw in the previous section, overriding methods may refine their “output” to some extent. Namely, they can narrow their
09 clause by declaring that they throw subtypes of the original method’s exception types. What about the main “output” of a method? Its return value? Can we change the return type of a method by overriding it? The answer is that Java gives us covariant return types on methods just as it does for exception types.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
What this means is that when you override a method, you may change the return type to a subtype of the original method’s return type. For example, if our
4 class has a factory method calledCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
42 that produces an instance ofclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
4, ourCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
1 class could refine the return type toCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
1:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
2class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
As we’ll see later, this coding technique is very helpful because it eliminates some runtime casting of objects.
Special References: this and super
The special references
46 andclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
4 allow you to refer to the members of the current object instance or to members of the superclass, respectively. We have seenCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
46 used elsewhere to pass a reference to the current object and to refer to shadowed instance variables. The referenceclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
4 does the same for the parents of a class. You can use it to refer to members of a superclass that have been shadowed or overridden. Being able to invoke the original method of the superclass allows us to use it as part of our new method, delegating to its behavior before or after we perform additional work:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
3class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
In this example, our
26 class overrides theclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
51 method to first do some checking on theclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
52 object. After doing its job, it usesclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
53 to call the [otherwise overridden and inaccessible] implementation ofclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
3 in its superclass.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 prompts a search for the method or variable to begin in the scope of the immediate superclass rather than the current class. The inherited method or variable found may reside in the immediate superclass or one further up the tree. The usage of theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
4 reference when applied to overridden methods of a superclass is special; it tells the method resolution system to stop the dynamic method search at the superclass instead of at the most derived class [as it otherwise does]. WithoutCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
4, there would be no way to access overridden methods.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
...
creature
.
sleep
[];
// accesses Cat sleep[];
A cast explicitly tells the compiler to change the apparent type of an object reference. The main use for casts is when an object is temporarily assigned to a more general type. For example, if a
58 were assigned to a variable of typeclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
59, to use it as aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
58 again, we’d have to perform a cast to get it back. The compiler recognizes only the declared types of variables and doesn’t know that we actually placed aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
58 into it. In Java, casts are checked both at compile time and at runtime to make sure they are legal. At compile time the Java compiler will stop you from trying to perform a cast that cannot possibly work [such as turning aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
62 directly into aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
58]. And at runtime, Java will check that casts that are plausible [such as ourclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
59 toclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
58] are actually correct for the real objects involved.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
Attempting to cast an object to an incompatible type at runtime results in a
66class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
67class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
68. Only casts between objects in the same inheritance hierarchy [and, as we’ll see later, to appropriate interfaces] are legal in Java and pass the scrutiny of the compiler and the runtime system. Casts in Java affect only the treatment of references; they never change the form of the actual object. This is an important rule to keep in mind. You never change the object pointed to by a reference by casting it; you change only the compiler’s [or runtime system’s] notion of it.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
A cast can be used to narrow or downcast the type of a reference—to make it more specific. Often, we’ll do this when we have to retrieve an object from a more general type of collection or when it has been previously used as a less derived type. [The prototypical example is using an object in a collection, as we’ll see in Chapter 11.] Continuing with our
5 example:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
We can’t reassign the reference in
8 to the variableclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
6 even though we know it holds an instance of aclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
5 [Simon]. We have to perform the indicated cast to narrow the reference. Note that an implicit cast was performed when we went the other way to widen the referenceCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
6 to typeclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
4 during the first assignment. In this case, an explicit cast would have been legal but superfluous.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
What all this means is that you can’t lie or guess about what an object is. If you have a
5 object, you can use it as anCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 or evenCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
59 because all Java classes are a subclass ofclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
59. But if you have anclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
59 you think is aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
5, you have to perform a cast to get it back to anCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
4 or aCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
5. If you aren’t sure whether theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
59 is aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
5 or aCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
85 at runtime, you can check it withclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
86 before you perform the cast. If you do not check and you get the cast wrong, the runtime system throws aclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
66class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
67class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
68.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
5class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
As we mentioned earlier, casting can affect the selection of compile-time items such as variables and overloaded methods, but not the selection of overridden methods. shows the difference. As shown in the top half of the diagram, casting the reference
6 to typeclass
IntegerCalculator
{
int
sum
;
...
}
class
DecimalCalculator
extends
IntegerCalculator
{
double
sum
;
...
}
4 [widening it] affects the selection of the shadowed variableCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
2 within it. However, as the lower half of the diagram indicates, the cast doesn’t affect the selection of the overridden methodCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
2.static
final
boolean
DEBUG
=
false
;
...
final
void
debug
[
String
message
]
{
if
[
DEBUG
]
{
System
.
err
.
println
[
message
];
// do other stuff
...
}
}
Figure 6-4. Casting and selection of methods and variables
Casting in Java is something that programmers strive to avoid. This is not only because it indicates a weakness in the static typing of the code, but because casts can also simply be tedious to use and make code less readable. Unfortunately, a great deal of code written in Java in the past has had no choice but to rely on casting so that it can work with any type of object the user requires. Java 5.0 introduced a major new language feature, generics, partly to address this issue. Generics allow Java code to be “typed” for a particular kind of object by the user, eliminating the need to cast in many situations. We’ll cover generics in detail in Chapter 8 and see how they reduce the need for casts in most Java code.
Using Superclass Constructors
When we talked earlier about constructors, we discussed how the special statement
94 invokes an overloaded constructor upon entry to another constructor. Similarly, the statementclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
95 explicitly invokes the constructor of a superclass. Of course, we also talked about how Java makes a chain of constructor calls that includes the superclass’s constructor, so why useclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
95 explicitly? When Java makes an implicit call to the superclass constructor, it calls the default constructor. If we want to invoke a superclass constructor that takes arguments, we have to do so explicitly usingclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
95.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
If we are going to call a superclass constructor with
95, it must be the first statement of our constructor, just asclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
94 must be the first call we make in an overloaded constructor. Here’s a simple example:class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
6class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
In this example, we use
95 to take advantage of the implementation of the superclass constructor and avoid duplicating the code to set up the object based on its name. In fact, because the classclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
01 doesn’t define a default [no arguments] constructor, we have no choice but to callCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
95 explicitly. Otherwise, the compiler would complain that it couldn’t find an appropriate default constructor to call. In other words, if you subclass a class whose constructors all take arguments, you have to invoke one of the superclass’s constructors explicitly from at least one of your subclass’s constructors.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
Instance variables of the class are initialized upon return from the superclass constructor, whether that’s due to an explicit call to
95 or an implicit call to the default superclass constructor.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
Full Disclosure: Constructors and Initialization
We can now tell the full story of how constructors are chained together and when instance variable initialization occurs. The rule has three parts and is applied repeatedly for each successive constructor that is invoked:
If the first statement of a constructor is an ordinary statement—that is, not a call to
94 orclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
95—Java inserts an implicit call toclass
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
95 to invoke the default constructor of the superclass. Upon returning from that call, Java initializes the instance variables of the current class and proceeds to execute the statements of the current constructor.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
If the first statement of a constructor is a call to a superclass constructor via
95, Java invokes the selected superclass constructor. Upon its return, Java initializes the current class’s instance variables and proceeds with the statements of the current constructor.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
If the first statement of a constructor is a call to an overloaded constructor via
94, Java invokes the selected constructor, and upon its return, simply proceeds with the statements of the current constructor. The call to the superclass’s constructor has happened within the overloaded constructor, either explicitly or implicitly, so the initialization of instance variables has already occurred.class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
Abstract Methods and Classes
A method in Java can be declared with the
09 modifier to indicate that it’s just a prototype. An abstract method has no body; it’s simply a signature declaration followed by a semicolon. You can’t directly use a class that contains an abstract method; you must instead create a subclass that implements the abstract method’s body:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
7class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
In Java, a class that contains one or more abstract methods must be explicitly declared as an abstract class, also using the
09 modifier:Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
8class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
An abstract class can contain other nonabstract methods and ordinary variable declarations, but it can’t be instantiated. To be used, it must be subclassed and its abstract methods must be “overridden” with methods that implement a body. Not all abstract methods have to be implemented in a single subclass, but a subclass that doesn’t override all its superclass’s abstract methods with actual, concrete implementations must also be declared
09.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
9class
Cat
extends
Mammal
{
// inherits weight and heartRate
boolean
longHair
;
...
// inherits eat[] and breathe[]
void
purr
[]
{
...
}
}
Abstract classes provide a framework for classes that is to be “filled in” by the implementer. The
12 class, for example, has a single abstract method calledCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
13. Various subclasses ofCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
14 implementCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
13 in their own ways to read from their own sources. The rest of theCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
14 class, however, provides extended functionality built on the simpleCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
13 method. A subclass ofCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
14 inherits these nonabstract methods to provide functionality based on the simpleCat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
13 method that the subclass implements.Cat
simon
=
new
Cat
[];
Animal
creature
=
simon
;
Can a subclass of a non abstract superclass can be abstract?
A subclass of a non-abstract superclass can be abstract. A subclass can override a concrete method in a superclass to define it as abstract. An abstract method cannot be static.Which class definition defines an abstract class correctly?
An abstract class is a template definition of methods and variables of a class [category of objects] that contains one or more abstracted methods. Abstract classes are used in all object-oriented programming [OOP] languages, including Java [see Java abstract class], C++, C# and VB.NET.Which of the following statements about abstract methods classes in Java are false?
False, We cannot create objects of an abstract class. To implement features of an abstract class, we inherit subclasses from it and create objects of the subclass.What does a subclass inherit from a superclass quizlet?
What does a subclass inherit from its superclass? It inherits all the superclass's attributes. Look at the fo llowing code, which is the first line of a class definition.